@memberjunction/ng-dashboard-viewer 5.43.0 → 5.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/lib/config-panels/artifact-config-panel.component.d.ts +0 -1
  2. package/dist/lib/config-panels/artifact-config-panel.component.d.ts.map +1 -1
  3. package/dist/lib/config-panels/artifact-config-panel.component.js +49 -63
  4. package/dist/lib/config-panels/artifact-config-panel.component.js.map +1 -1
  5. package/dist/lib/config-panels/query-config-panel.component.d.ts +0 -1
  6. package/dist/lib/config-panels/query-config-panel.component.d.ts.map +1 -1
  7. package/dist/lib/config-panels/query-config-panel.component.js +110 -124
  8. package/dist/lib/config-panels/query-config-panel.component.js.map +1 -1
  9. package/dist/lib/config-panels/view-config-panel.component.d.ts +0 -2
  10. package/dist/lib/config-panels/view-config-panel.component.d.ts.map +1 -1
  11. package/dist/lib/config-panels/view-config-panel.component.js +142 -171
  12. package/dist/lib/config-panels/view-config-panel.component.js.map +1 -1
  13. package/dist/lib/config-panels/weburl-config-panel.component.js +2 -2
  14. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts +4 -0
  15. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts.map +1 -1
  16. package/dist/lib/dashboard-browser/dashboard-browser.component.js +205 -272
  17. package/dist/lib/dashboard-browser/dashboard-browser.component.js.map +1 -1
  18. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts +46 -1
  19. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts.map +1 -1
  20. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js +229 -49
  21. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js.map +1 -1
  22. package/dist/lib/dashboard-viewer.module.d.ts +2 -1
  23. package/dist/lib/dashboard-viewer.module.d.ts.map +1 -1
  24. package/dist/lib/dashboard-viewer.module.js +11 -3
  25. package/dist/lib/dashboard-viewer.module.js.map +1 -1
  26. package/dist/lib/parts/artifact-part.component.d.ts.map +1 -1
  27. package/dist/lib/parts/artifact-part.component.js +25 -32
  28. package/dist/lib/parts/artifact-part.component.js.map +1 -1
  29. package/dist/lib/parts/query-part.component.d.ts.map +1 -1
  30. package/dist/lib/parts/query-part.component.js +25 -32
  31. package/dist/lib/parts/query-part.component.js.map +1 -1
  32. package/dist/lib/parts/view-part.component.d.ts.map +1 -1
  33. package/dist/lib/parts/view-part.component.js +25 -32
  34. package/dist/lib/parts/view-part.component.js.map +1 -1
  35. package/dist/lib/parts/weburl-part.component.d.ts.map +1 -1
  36. package/dist/lib/parts/weburl-part.component.js +46 -52
  37. package/dist/lib/parts/weburl-part.component.js.map +1 -1
  38. package/package.json +13 -11
  39. package/dist/__tests__/exports.test.d.ts +0 -2
  40. package/dist/__tests__/exports.test.d.ts.map +0 -1
  41. package/dist/__tests__/exports.test.js +0 -17
  42. package/dist/__tests__/exports.test.js.map +0 -1
  43. package/dist/__tests__/index.test.d.ts +0 -2
  44. package/dist/__tests__/index.test.d.ts.map +0 -1
  45. package/dist/__tests__/index.test.js +0 -7
  46. package/dist/__tests__/index.test.js.map +0 -1
@@ -9,6 +9,7 @@ import { RegisterClass } from '@memberjunction/global';
9
9
  import { BaseDashboardPart } from './base-dashboard-part';
10
10
  import * as i0 from "@angular/core";
11
11
  import * as i1 from "@angular/platform-browser";
12
+ import * as i2 from "@memberjunction/ng-ui-components";
12
13
  const _c0 = ["iframe"];
13
14
  function WebURLPartComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
14
15
  i0.ɵɵelementStart(0, "div", 2);
@@ -17,9 +18,9 @@ function WebURLPartComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
17
18
  i0.ɵɵtext(3, "Loading...");
18
19
  i0.ɵɵelementEnd()();
19
20
  } }
20
- function WebURLPartComponent_Conditional_2_Conditional_4_Template(rf, ctx) { if (rf & 1) {
21
- i0.ɵɵelementStart(0, "a", 8);
22
- i0.ɵɵelement(1, "i", 9);
21
+ function WebURLPartComponent_Conditional_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
22
+ i0.ɵɵelementStart(0, "a", 7);
23
+ i0.ɵɵelement(1, "i", 8);
23
24
  i0.ɵɵtext(2, " Open in new window ");
24
25
  i0.ɵɵelementEnd();
25
26
  } if (rf & 2) {
@@ -27,33 +28,21 @@ function WebURLPartComponent_Conditional_2_Conditional_4_Template(rf, ctx) { if
27
28
  i0.ɵɵproperty("href", ctx_r0.rawUrl, i0.ɵɵsanitizeUrl);
28
29
  } }
29
30
  function WebURLPartComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
30
- i0.ɵɵelementStart(0, "div", 3);
31
- i0.ɵɵelement(1, "i", 7);
32
- i0.ɵɵelementStart(2, "span");
33
- i0.ɵɵtext(3);
34
- i0.ɵɵelementEnd();
35
- i0.ɵɵconditionalCreate(4, WebURLPartComponent_Conditional_2_Conditional_4_Template, 3, 1, "a", 8);
31
+ i0.ɵɵelementStart(0, "mj-empty-state", 3);
32
+ i0.ɵɵconditionalCreate(1, WebURLPartComponent_Conditional_2_Conditional_1_Template, 3, 1, "a", 7);
36
33
  i0.ɵɵelementEnd();
37
34
  } if (rf & 2) {
38
35
  const ctx_r0 = i0.ɵɵnextContext();
39
- i0.ɵɵadvance(3);
40
- i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
36
+ i0.ɵɵproperty("Message", ctx_r0.ErrorMessage);
41
37
  i0.ɵɵadvance();
42
- i0.ɵɵconditional(ctx_r0.rawUrl ? 4 : -1);
38
+ i0.ɵɵconditional(ctx_r0.rawUrl ? 1 : -1);
43
39
  } }
44
40
  function WebURLPartComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
45
- i0.ɵɵelementStart(0, "div", 4);
46
- i0.ɵɵelement(1, "i", 10);
47
- i0.ɵɵelementStart(2, "h4");
48
- i0.ɵɵtext(3, "No URL Configured");
49
- i0.ɵɵelementEnd();
50
- i0.ɵɵelementStart(4, "p");
51
- i0.ɵɵtext(5, "Click the configure button to set a URL for this part.");
52
- i0.ɵɵelementEnd()();
41
+ i0.ɵɵelement(0, "mj-empty-state", 4);
53
42
  } }
54
43
  function WebURLPartComponent_Conditional_4_Conditional_0_Template(rf, ctx) { if (rf & 1) {
55
44
  const _r2 = i0.ɵɵgetCurrentView();
56
- i0.ɵɵelementStart(0, "iframe", 17, 0);
45
+ i0.ɵɵelementStart(0, "iframe", 15, 0);
57
46
  i0.ɵɵlistener("load", function WebURLPartComponent_Conditional_4_Conditional_0_Template_iframe_load_0_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeLoad()); })("error", function WebURLPartComponent_Conditional_4_Conditional_0_Template_iframe_error_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeError($event)); });
58
47
  i0.ɵɵelementEnd();
59
48
  } if (rf & 2) {
@@ -62,7 +51,7 @@ function WebURLPartComponent_Conditional_4_Conditional_0_Template(rf, ctx) { if
62
51
  } }
63
52
  function WebURLPartComponent_Conditional_4_Conditional_1_Template(rf, ctx) { if (rf & 1) {
64
53
  const _r3 = i0.ɵɵgetCurrentView();
65
- i0.ɵɵelementStart(0, "iframe", 18, 0);
54
+ i0.ɵɵelementStart(0, "iframe", 16, 0);
66
55
  i0.ɵɵlistener("load", function WebURLPartComponent_Conditional_4_Conditional_1_Template_iframe_load_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeLoad()); })("error", function WebURLPartComponent_Conditional_4_Conditional_1_Template_iframe_error_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeError($event)); });
67
56
  i0.ɵɵelementEnd();
68
57
  } if (rf & 2) {
@@ -71,7 +60,7 @@ function WebURLPartComponent_Conditional_4_Conditional_1_Template(rf, ctx) { if
71
60
  } }
72
61
  function WebURLPartComponent_Conditional_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
73
62
  const _r4 = i0.ɵɵgetCurrentView();
74
- i0.ɵɵelementStart(0, "iframe", 19, 0);
63
+ i0.ɵɵelementStart(0, "iframe", 17, 0);
75
64
  i0.ɵɵlistener("load", function WebURLPartComponent_Conditional_4_Conditional_2_Template_iframe_load_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeLoad()); })("error", function WebURLPartComponent_Conditional_4_Conditional_2_Template_iframe_error_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeError($event)); });
76
65
  i0.ɵɵelementEnd();
77
66
  } if (rf & 2) {
@@ -80,7 +69,7 @@ function WebURLPartComponent_Conditional_4_Conditional_2_Template(rf, ctx) { if
80
69
  } }
81
70
  function WebURLPartComponent_Conditional_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
82
71
  const _r5 = i0.ɵɵgetCurrentView();
83
- i0.ɵɵelementStart(0, "iframe", 20, 0);
72
+ i0.ɵɵelementStart(0, "iframe", 18, 0);
84
73
  i0.ɵɵlistener("load", function WebURLPartComponent_Conditional_4_Conditional_3_Template_iframe_load_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeLoad()); })("error", function WebURLPartComponent_Conditional_4_Conditional_3_Template_iframe_error_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeError($event)); });
85
74
  i0.ɵɵelementEnd();
86
75
  } if (rf & 2) {
@@ -89,7 +78,7 @@ function WebURLPartComponent_Conditional_4_Conditional_3_Template(rf, ctx) { if
89
78
  } }
90
79
  function WebURLPartComponent_Conditional_4_Conditional_4_Template(rf, ctx) { if (rf & 1) {
91
80
  const _r6 = i0.ɵɵgetCurrentView();
92
- i0.ɵɵelementStart(0, "iframe", 21, 0);
81
+ i0.ɵɵelementStart(0, "iframe", 19, 0);
93
82
  i0.ɵɵlistener("load", function WebURLPartComponent_Conditional_4_Conditional_4_Template_iframe_load_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeLoad()); })("error", function WebURLPartComponent_Conditional_4_Conditional_4_Template_iframe_error_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeError($event)); });
94
83
  i0.ɵɵelementEnd();
95
84
  } if (rf & 2) {
@@ -98,7 +87,7 @@ function WebURLPartComponent_Conditional_4_Conditional_4_Template(rf, ctx) { if
98
87
  } }
99
88
  function WebURLPartComponent_Conditional_4_Conditional_5_Template(rf, ctx) { if (rf & 1) {
100
89
  const _r7 = i0.ɵɵgetCurrentView();
101
- i0.ɵɵelementStart(0, "iframe", 22, 0);
90
+ i0.ɵɵelementStart(0, "iframe", 20, 0);
102
91
  i0.ɵɵlistener("load", function WebURLPartComponent_Conditional_4_Conditional_5_Template_iframe_load_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeLoad()); })("error", function WebURLPartComponent_Conditional_4_Conditional_5_Template_iframe_error_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onIframeError($event)); });
103
92
  i0.ɵɵelementEnd();
104
93
  } if (rf & 2) {
@@ -106,12 +95,12 @@ function WebURLPartComponent_Conditional_4_Conditional_5_Template(rf, ctx) { if
106
95
  i0.ɵɵproperty("src", ctx_r0.SafeUrl, i0.ɵɵsanitizeResourceUrl)("title", (ctx_r0.Panel == null ? null : ctx_r0.Panel.title) || "Embedded content");
107
96
  } }
108
97
  function WebURLPartComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
109
- i0.ɵɵconditionalCreate(0, WebURLPartComponent_Conditional_4_Conditional_0_Template, 2, 2, "iframe", 11);
110
- i0.ɵɵconditionalCreate(1, WebURLPartComponent_Conditional_4_Conditional_1_Template, 2, 2, "iframe", 12);
111
- i0.ɵɵconditionalCreate(2, WebURLPartComponent_Conditional_4_Conditional_2_Template, 2, 2, "iframe", 13);
112
- i0.ɵɵconditionalCreate(3, WebURLPartComponent_Conditional_4_Conditional_3_Template, 2, 2, "iframe", 14);
113
- i0.ɵɵconditionalCreate(4, WebURLPartComponent_Conditional_4_Conditional_4_Template, 2, 2, "iframe", 15);
114
- i0.ɵɵconditionalCreate(5, WebURLPartComponent_Conditional_4_Conditional_5_Template, 2, 2, "iframe", 16);
98
+ i0.ɵɵconditionalCreate(0, WebURLPartComponent_Conditional_4_Conditional_0_Template, 2, 2, "iframe", 9);
99
+ i0.ɵɵconditionalCreate(1, WebURLPartComponent_Conditional_4_Conditional_1_Template, 2, 2, "iframe", 10);
100
+ i0.ɵɵconditionalCreate(2, WebURLPartComponent_Conditional_4_Conditional_2_Template, 2, 2, "iframe", 11);
101
+ i0.ɵɵconditionalCreate(3, WebURLPartComponent_Conditional_4_Conditional_3_Template, 2, 2, "iframe", 12);
102
+ i0.ɵɵconditionalCreate(4, WebURLPartComponent_Conditional_4_Conditional_4_Template, 2, 2, "iframe", 13);
103
+ i0.ɵɵconditionalCreate(5, WebURLPartComponent_Conditional_4_Conditional_5_Template, 2, 2, "iframe", 14);
115
104
  } if (rf & 2) {
116
105
  const ctx_r0 = i0.ɵɵnextContext();
117
106
  i0.ɵɵconditional(ctx_r0.sandboxMode === "standard" && ctx_r0.allowFullscreen ? 0 : -1);
@@ -130,8 +119,8 @@ function WebURLPartComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
130
119
  i0.ɵɵelementStart(0, "div", 5)(1, "span");
131
120
  i0.ɵɵtext(2, "If the content doesn't load:");
132
121
  i0.ɵɵelementEnd();
133
- i0.ɵɵelementStart(3, "a", 23);
134
- i0.ɵɵelement(4, "i", 9);
122
+ i0.ɵɵelementStart(3, "a", 21);
123
+ i0.ɵɵelement(4, "i", 22);
135
124
  i0.ɵɵtext(5, " Open in new window ");
136
125
  i0.ɵɵelementEnd()();
137
126
  } if (rf & 2) {
@@ -243,11 +232,11 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
243
232
  } if (rf & 2) {
244
233
  let _t;
245
234
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.iframeRef = _t.first);
246
- } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 9, consts: [["iframe", ""], [1, "weburl-part"], [1, "loading-state"], [1, "error-state"], [1, "empty-state"], [1, "iframe-fallback"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-exclamation-triangle"], ["target", "_blank", 1, "open-link", 3, "href"], [1, "fa-solid", "fa-external-link-alt"], [1, "fa-solid", "fa-globe"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "load", "error", "src", "title"], ["target", "_blank", 3, "href"]], template: function WebURLPartComponent_Template(rf, ctx) { if (rf & 1) {
235
+ } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 9, consts: [["iframe", ""], [1, "weburl-part"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load content", "Size", "compact", 1, "part-placeholder", 3, "Message"], ["Icon", "fa-solid fa-globe", "Title", "No URL Configured", "Message", "Click the configure button to set a URL for this part.", "Size", "compact", 1, "part-placeholder"], [1, "iframe-fallback"], [1, "fa-solid", "fa-spinner", "fa-spin"], ["actions", "", "target", "_blank", 1, "open-link", 3, "href"], [1, "fa-solid", "fa-up-right-from-square"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", "allowfullscreen", "", 1, "content-iframe", 3, "load", "error", "src", "title"], ["sandbox", "allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation", 1, "content-iframe", 3, "load", "error", "src", "title"], ["target", "_blank", 3, "href"], [1, "fa-solid", "fa-external-link-alt"]], template: function WebURLPartComponent_Template(rf, ctx) { if (rf & 1) {
247
236
  i0.ɵɵelementStart(0, "div", 1);
248
237
  i0.ɵɵconditionalCreate(1, WebURLPartComponent_Conditional_1_Template, 4, 0, "div", 2);
249
- i0.ɵɵconditionalCreate(2, WebURLPartComponent_Conditional_2_Template, 5, 2, "div", 3);
250
- i0.ɵɵconditionalCreate(3, WebURLPartComponent_Conditional_3_Template, 6, 0, "div", 4);
238
+ i0.ɵɵconditionalCreate(2, WebURLPartComponent_Conditional_2_Template, 2, 2, "mj-empty-state", 3);
239
+ i0.ɵɵconditionalCreate(3, WebURLPartComponent_Conditional_3_Template, 1, 0, "mj-empty-state", 4);
251
240
  i0.ɵɵconditionalCreate(4, WebURLPartComponent_Conditional_4_Template, 6, 6);
252
241
  i0.ɵɵconditionalCreate(5, WebURLPartComponent_Conditional_5_Template, 6, 1, "div", 5);
253
242
  i0.ɵɵelementEnd();
@@ -263,7 +252,7 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
263
252
  i0.ɵɵconditional(!ctx.IsLoading && !ctx.ErrorMessage && ctx.SafeUrl ? 4 : -1);
264
253
  i0.ɵɵadvance();
265
254
  i0.ɵɵconditional(!ctx.IsLoading && !ctx.ErrorMessage && ctx.SafeUrl && ctx.showFallbackLink ? 5 : -1);
266
- } }, styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; \n\n }\n\n .loading-state[_ngcontent-%COMP%], \n .error-state[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .error-state[_ngcontent-%COMP%] .open-link[_ngcontent-%COMP%] {\n margin-top: 16px;\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .error-state[_ngcontent-%COMP%] .open-link[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .empty-state[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n }\n\n .content-iframe[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback[_ngcontent-%COMP%] a[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback[_ngcontent-%COMP%] a[_ngcontent-%COMP%]:hover {\n text-decoration: underline;\n }"] });
255
+ } }, dependencies: [i2.MJEmptyStateComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; \n\n }\n\n .loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .part-placeholder[_ngcontent-%COMP%] {\n height: 100%;\n }\n\n \n\n .open-link[_ngcontent-%COMP%] {\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .open-link[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .content-iframe[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback[_ngcontent-%COMP%] a[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback[_ngcontent-%COMP%] a[_ngcontent-%COMP%]:hover {\n text-decoration: underline;\n }"] });
267
256
  };
268
257
  WebURLPartComponent = __decorate([
269
258
  RegisterClass(BaseDashboardPart, 'WebURLPanelRenderer')
@@ -283,25 +272,30 @@ export { WebURLPartComponent };
283
272
 
284
273
  <!-- Error state -->
285
274
  @if (ErrorMessage) {
286
- <div class="error-state">
287
- <i class="fa-solid fa-exclamation-triangle"></i>
288
- <span>{{ ErrorMessage }}</span>
275
+ <mj-empty-state
276
+ class="part-placeholder"
277
+ Variant="error"
278
+ Icon="fa-solid fa-triangle-exclamation"
279
+ Title="Couldn't load content"
280
+ [Message]="ErrorMessage"
281
+ Size="compact">
289
282
  @if (rawUrl) {
290
- <a [href]="rawUrl" target="_blank" class="open-link">
291
- <i class="fa-solid fa-external-link-alt"></i>
283
+ <a actions [href]="rawUrl" target="_blank" class="open-link">
284
+ <i class="fa-solid fa-up-right-from-square"></i>
292
285
  Open in new window
293
286
  </a>
294
287
  }
295
- </div>
288
+ </mj-empty-state>
296
289
  }
297
-
290
+
298
291
  <!-- No URL configured -->
299
292
  @if (!IsLoading && !ErrorMessage && !SafeUrl) {
300
- <div class="empty-state">
301
- <i class="fa-solid fa-globe"></i>
302
- <h4>No URL Configured</h4>
303
- <p>Click the configure button to set a URL for this part.</p>
304
- </div>
293
+ <mj-empty-state
294
+ class="part-placeholder"
295
+ Icon="fa-solid fa-globe"
296
+ Title="No URL Configured"
297
+ Message="Click the configure button to set a URL for this part."
298
+ Size="compact" />
305
299
  }
306
300
 
307
301
  <!-- Iframe container - sandbox and allowfullscreen must be static, so we use ng-container to switch -->
@@ -394,10 +388,10 @@ export { WebURLPartComponent };
394
388
  </div>
395
389
  }
396
390
  </div>
397
- `, styles: ["\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; /* Required for overlay positioning */\n }\n\n .loading-state,\n .error-state,\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state i,\n .error-state i,\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state i {\n color: var(--mj-status-error);\n }\n\n .error-state .open-link {\n margin-top: 16px;\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .error-state .open-link:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .empty-state h4 {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n .content-iframe {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback a:hover {\n text-decoration: underline;\n }\n "] }]
391
+ `, styles: ["\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; /* Required for overlay positioning */\n }\n\n .loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .part-placeholder {\n height: 100%;\n }\n\n /* Projected \"open in new window\" link inside the error empty-state */\n .open-link {\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .open-link:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .content-iframe {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback a:hover {\n text-decoration: underline;\n }\n "] }]
398
392
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i1.DomSanitizer }], { iframeRef: [{
399
393
  type: ViewChild,
400
394
  args: ['iframe']
401
395
  }] }); })();
402
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WebURLPartComponent, { className: "WebURLPartComponent", filePath: "src/lib/parts/weburl-part.component.ts", lineNumber: 239 }); })();
396
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WebURLPartComponent, { className: "WebURLPartComponent", filePath: "src/lib/parts/weburl-part.component.ts", lineNumber: 230 }); })();
403
397
  //# sourceMappingURL=weburl-part.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"weburl-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAiC,SAAS,EAAiB,MAAM,eAAe,CAAC;AAEnG,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;;;;;IAe9C,8BAA2B;IACzB,uBAA2C;IAC3C,4BAAM;IAAA,0BAAU;IAClB,AADkB,iBAAO,EACnB;;;IASF,4BAAqD;IACnD,uBAA6C;IAC7C,oCACF;IAAA,iBAAI;;;IAHD,sDAAe;;;IAJtB,8BAAyB;IACvB,uBAAgD;IAChD,4BAAM;IAAA,YAAkB;IAAA,iBAAO;IAC/B,iGAAc;IAMhB,iBAAM;;;IAPE,eAAkB;IAAlB,yCAAkB;IACxB,cAKC;IALD,wCAKC;;;IAMH,8BAAyB;IACvB,wBAAiC;IACjC,0BAAI;IAAA,iCAAiB;IAAA,iBAAK;IAC1B,yBAAG;IAAA,sEAAsD;IAC3D,AAD2D,iBAAI,EACzD;;;;IAOJ,qCAQyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAHA,8DAAe,mFAG6B;;;;IAQ9C,qCAOyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAFA,8DAAe,mFAE6B;;;;IAQ9C,qCAQyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAHA,8DAAe,mFAG6B;;;;IAQ9C,qCAOyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAFA,8DAAe,mFAE6B;;;;IAQ9C,qCAQyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAHA,8DAAe,mFAG6B;;;;IAQ9C,qCAOyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAFA,8DAAe,mFAE6B;;;IApEhD,uGAAqD;IAarD,uGAAsD;IAYtD,uGAAmD;IAanD,uGAAoD;IAYpD,uGAAuD;IAavD,uGAAwD;;;IA/DxD,sFAWC;IAED,cAUC;IAVD,uFAUC;IAED,cAWC;IAXD,oFAWC;IAED,cAUC;IAVD,qFAUC;IAED,cAWC;IAXD,wFAWC;IAED,cAUC;IAVD,yFAUC;;;IAMC,AADF,8BAA6B,WACrB;IAAA,4CAA4B;IAAA,iBAAO;IACzC,6BAAmC;IACjC,uBAA6C;IAC7C,oCACF;IACF,AADE,iBAAI,EACA;;;IAJD,eAAe;IAAf,sDAAe;;AA5HhC;;;GAGG;AAqOI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,iBAAiB;IACjC,SAAS,CAAiC;IAExD,OAAO,GAA2B,IAAI,CAAC;IACvC,MAAM,GAAW,EAAE,CAAC;IACpB,WAAW,GAAyC,UAAU,CAAC;IAC/D,eAAe,GAAY,IAAI,CAAC;IAChC,gBAAgB,GAAY,KAAK,CAAC,CAAC,uDAAuD;IAEzF,SAAS,CAAe;IACxB,gBAAgB,GAAyC,IAAI,CAAC;IAC9D,YAAY,GAAY,KAAK,CAAC;IAEtC,YAAY,GAAsB,EAAE,SAAuB;QACvD,KAAK,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,eAAe;QACX,uCAAuC;QACvC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,WAAW;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAe,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,KAAK,CAAuB,CAAC;QAElD,6BAA6B;QAC7B,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACpC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QACjC,CAAC;QAED,cAAc;QACd,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAE9B,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,IAAI,CAAC;YACD,kCAAkC;YAClC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;YAElB,8CAA8C;YAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,8BAA8B,CAAC,GAAG,CAAC,CAAC;YAElE,+DAA+D;YAC/D,IAAI,CAAC,WAAW,GAAI,MAAM,EAAE,CAAC,aAAa,CAA0C,IAAI,UAAU,CAAC;YAEnG,4BAA4B;YAC5B,IAAI,CAAC,eAAe,GAAI,MAAM,EAAE,CAAC,iBAAiB,CAAa,IAAI,IAAI,CAAC;YAExE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAEvB,iEAAiE;YACjE,kFAAkF;YAClF,gGAAgG;YAChG,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,GAAG,EAAE;gBACpC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,CAAC,EAAE,IAAI,CAAC,CAAC;QAEb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC;QACjF,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,cAAc;QAClB,6EAA6E;QAC7E,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACrB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QACD,8EAA8E;IAClF,CAAC;IAED;;OAEG;IACI,YAAY;QACf,iCAAiC;QACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAEzB,4CAA4C;QAC5C,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACpC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QACjC,CAAC;QAED,+DAA+D;QAC/D,4DAA4D;QAC5D,iFAAiF;QACjF,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED;;OAEG;IACI,aAAa,CAAC,MAAa;QAC9B,oEAAoE;QACpE,IAAI,CAAC,QAAQ,CAAC,mFAAmF,CAAC,CAAC;IACvG,CAAC;6GAjHQ,mBAAmB;6DAAnB,mBAAmB;;;;;;YA/NxB,8BAAkF;YAEhF,qFAAiB;YAQjB,qFAAoB;YAcpB,qFAA+C;YAS/C,2EAA8C;YA+E9C,qFAAkE;YASpE,iBAAM;;YAzH+C,AAA5B,wCAA2B,2BAA6B;YAE/E,cAKC;YALD,wCAKC;YAGD,cAWC;YAXD,2CAWC;YAGD,cAMC;YAND,8EAMC;YAGD,cA4EC;YA5ED,6EA4EC;YAGD,cAQC;YARD,qGAQC;;;AAuGE,mBAAmB;IApO/B,aAAa,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;GAoO3C,mBAAmB,CAkH/B;;iFAlHY,mBAAmB;cAnO/B,SAAS;6BACI,KAAK,YACL,gBAAgB,YAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SA2HL;;kBAsGJ,SAAS;mBAAC,QAAQ;;kFADV,mBAAmB","sourcesContent":["import { Component, ChangeDetectorRef, ElementRef, ViewChild, AfterViewInit } from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\n\n/**\n * Runtime renderer for WebURL dashboard parts.\n * Displays web content in an iframe with configurable sandbox permissions.\n */\n@RegisterClass(BaseDashboardPart, 'WebURLPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-weburl-part',\n template: `\n <div class=\"weburl-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading...</span>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ ErrorMessage }}</span>\n @if (rawUrl) {\n <a [href]=\"rawUrl\" target=\"_blank\" class=\"open-link\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n Open in new window\n </a>\n }\n </div>\n }\n \n <!-- No URL configured -->\n @if (!IsLoading && !ErrorMessage && !SafeUrl) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-globe\"></i>\n <h4>No URL Configured</h4>\n <p>Click the configure button to set a URL for this part.</p>\n </div>\n }\n \n <!-- Iframe container - sandbox and allowfullscreen must be static, so we use ng-container to switch -->\n @if (!IsLoading && !ErrorMessage && SafeUrl) {\n <!-- Standard sandbox + fullscreen enabled -->\n @if (sandboxMode === 'standard' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Standard sandbox + fullscreen disabled -->\n @if (sandboxMode === 'standard' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen enabled -->\n @if (sandboxMode === 'strict' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen disabled -->\n @if (sandboxMode === 'strict' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen enabled -->\n @if (sandboxMode === 'permissive' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen disabled -->\n @if (sandboxMode === 'permissive' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n }\n \n <!-- Fallback link shown below iframe if content might be blocked -->\n @if (!IsLoading && !ErrorMessage && SafeUrl && showFallbackLink) {\n <div class=\"iframe-fallback\">\n <span>If the content doesn't load:</span>\n <a [href]=\"rawUrl\" target=\"_blank\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n Open in new window\n </a>\n </div>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; /* Required for overlay positioning */\n }\n\n .loading-state,\n .error-state,\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state i,\n .error-state i,\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state i {\n color: var(--mj-status-error);\n }\n\n .error-state .open-link {\n margin-top: 16px;\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .error-state .open-link:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .empty-state h4 {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n .content-iframe {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback a:hover {\n text-decoration: underline;\n }\n `]\n})\nexport class WebURLPartComponent extends BaseDashboardPart implements AfterViewInit {\n @ViewChild('iframe') iframeRef!: ElementRef<HTMLIFrameElement>;\n\n public SafeUrl: SafeResourceUrl | null = null;\n public rawUrl: string = '';\n public sandboxMode: 'standard' | 'strict' | 'permissive' = 'standard';\n public allowFullscreen: boolean = true;\n public showFallbackLink: boolean = false; // Hidden by default, shown if content might be blocked\n\n private sanitizer: DomSanitizer;\n private loadCheckTimeout: ReturnType<typeof setTimeout> | null = null;\n private iframeLoaded: boolean = false;\n\n constructor(cdr: ChangeDetectorRef, sanitizer: DomSanitizer) {\n super(cdr);\n this.sanitizer = sanitizer;\n }\n\n ngAfterViewInit(): void {\n // Initial load if panel is already set\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const url = config?.['url'] as string | undefined;\n\n // Clear any existing timeout\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // Reset state\n this.iframeLoaded = false;\n this.showFallbackLink = false;\n\n if (!url) {\n this.SafeUrl = null;\n this.rawUrl = '';\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n // Store raw URL for fallback link\n this.rawUrl = url;\n\n // Sanitize and set URL for iframe src binding\n this.SafeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);\n\n // Set sandbox mode (used by template to select correct iframe)\n this.sandboxMode = (config?.['sandboxMode'] as 'standard' | 'strict' | 'permissive') || 'standard';\n\n // Set fullscreen permission\n this.allowFullscreen = (config?.['allowFullscreen'] as boolean) ?? true;\n\n this.setLoading(false);\n\n // Set up a delayed check to detect if embedding might be blocked\n // If after 3 seconds the iframe hasn't successfully loaded content we can access,\n // show the blocked overlay. This is a heuristic since we can't detect X-Frame-Options directly.\n this.loadCheckTimeout = setTimeout(() => {\n this.checkIfBlocked();\n }, 3000);\n\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load URL');\n }\n }\n\n /**\n * Check if the iframe content might be blocked.\n * Called after a timeout - if the iframe load event hasn't fired, show the fallback link.\n */\n private checkIfBlocked(): void {\n // If the load event hasn't fired after the timeout, content might be blocked\n if (!this.iframeLoaded) {\n this.showFallbackLink = true;\n this.cdr.detectChanges();\n }\n // If iframeLoaded is true, content loaded successfully - keep fallback hidden\n }\n\n /**\n * Handle iframe load event\n */\n public onIframeLoad(): void {\n // Mark that the load event fired\n this.iframeLoaded = true;\n\n // Clear the timeout since the iframe loaded\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // The iframe loaded something - could still be blocked content\n // We can't reliably detect X-Frame-Options blocking from JS\n // The fallback link provides a way for users to open the site if it doesn't load\n this.cdr.detectChanges();\n }\n\n /**\n * Handle iframe error event\n */\n public onIframeError(_event: Event): void {\n // This may fire for some load failures, but not for X-Frame-Options\n this.setError('This site cannot be embedded. It may block iframe embedding for security reasons.');\n }\n}\n"]}
1
+ {"version":3,"file":"weburl-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAiC,SAAS,EAAiB,MAAM,eAAe,CAAC;AAEnG,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;;;;;;IAe9C,8BAA2B;IACzB,uBAA2C;IAC3C,4BAAM;IAAA,0BAAU;IAClB,AADkB,iBAAO,EACnB;;;IAaF,4BAA6D;IAC3D,uBAAgD;IAChD,oCACF;IAAA,iBAAI;;;IAHO,sDAAe;;;IAR9B,yCAMiB;IACf,iGAAc;IAMhB,iBAAiB;;;IARf,6CAAwB;IAExB,cAKC;IALD,wCAKC;;;IAMH,oCAKmB;;;;IAOjB,qCAQyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAHA,8DAAe,mFAG6B;;;;IAQ9C,qCAOyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAFA,8DAAe,mFAE6B;;;;IAQ9C,qCAQyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAHA,8DAAe,mFAG6B;;;;IAQ9C,qCAOyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAFA,8DAAe,mFAE6B;;;;IAQ9C,qCAQyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAHA,8DAAe,mFAG6B;;;;IAQ9C,qCAOyB;IADvB,AADA,oMAAQ,qBAAc,KAAC,+LACd,4BAAqB,KAAC;IAEjC,iBAAS;;;IAJP,AAFA,8DAAe,mFAE6B;;;IApEhD,sGAAqD;IAarD,uGAAsD;IAYtD,uGAAmD;IAanD,uGAAoD;IAYpD,uGAAuD;IAavD,uGAAwD;;;IA/DxD,sFAWC;IAED,cAUC;IAVD,uFAUC;IAED,cAWC;IAXD,oFAWC;IAED,cAUC;IAVD,qFAUC;IAED,cAWC;IAXD,wFAWC;IAED,cAUC;IAVD,yFAUC;;;IAMC,AADF,8BAA6B,WACrB;IAAA,4CAA4B;IAAA,iBAAO;IACzC,6BAAmC;IACjC,wBAA6C;IAC7C,oCACF;IACF,AADE,iBAAI,EACA;;;IAJD,eAAe;IAAf,sDAAe;;AAjIhC;;;GAGG;AA4NI,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,iBAAiB;IACjC,SAAS,CAAiC;IAExD,OAAO,GAA2B,IAAI,CAAC;IACvC,MAAM,GAAW,EAAE,CAAC;IACpB,WAAW,GAAyC,UAAU,CAAC;IAC/D,eAAe,GAAY,IAAI,CAAC;IAChC,gBAAgB,GAAY,KAAK,CAAC,CAAC,uDAAuD;IAEzF,SAAS,CAAe;IACxB,gBAAgB,GAAyC,IAAI,CAAC;IAC9D,YAAY,GAAY,KAAK,CAAC;IAEtC,YAAY,GAAsB,EAAE,SAAuB;QACvD,KAAK,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,eAAe;QACX,uCAAuC;QACvC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,WAAW;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAe,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,KAAK,CAAuB,CAAC;QAElD,6BAA6B;QAC7B,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACpC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QACjC,CAAC;QAED,cAAc;QACd,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAE9B,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;YACzB,OAAO;QACX,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,IAAI,CAAC;YACD,kCAAkC;YAClC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;YAElB,8CAA8C;YAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,8BAA8B,CAAC,GAAG,CAAC,CAAC;YAElE,+DAA+D;YAC/D,IAAI,CAAC,WAAW,GAAI,MAAM,EAAE,CAAC,aAAa,CAA0C,IAAI,UAAU,CAAC;YAEnG,4BAA4B;YAC5B,IAAI,CAAC,eAAe,GAAI,MAAM,EAAE,CAAC,iBAAiB,CAAa,IAAI,IAAI,CAAC;YAExE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAEvB,iEAAiE;YACjE,kFAAkF;YAClF,gGAAgG;YAChG,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,GAAG,EAAE;gBACpC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,CAAC,EAAE,IAAI,CAAC,CAAC;QAEb,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC;QACjF,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,cAAc;QAClB,6EAA6E;QAC7E,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACrB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC7B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QACD,8EAA8E;IAClF,CAAC;IAED;;OAEG;IACI,YAAY;QACf,iCAAiC;QACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAEzB,4CAA4C;QAC5C,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,YAAY,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACpC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QACjC,CAAC;QAED,+DAA+D;QAC/D,4DAA4D;QAC5D,iFAAiF;QACjF,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED;;OAEG;IACI,aAAa,CAAC,MAAa;QAC9B,oEAAoE;QACpE,IAAI,CAAC,QAAQ,CAAC,mFAAmF,CAAC,CAAC;IACvG,CAAC;6GAjHQ,mBAAmB;6DAAnB,mBAAmB;;;;;;YAtNxB,8BAAkF;YAEhF,qFAAiB;YAQjB,gGAAoB;YAkBpB,gGAA+C;YAU/C,2EAA8C;YA+E9C,qFAAkE;YASpE,iBAAM;;YA9H+C,AAA5B,wCAA2B,2BAA6B;YAE/E,cAKC;YALD,wCAKC;YAGD,cAeC;YAfD,2CAeC;YAGD,cAOC;YAPD,8EAOC;YAGD,cA4EC;YA5ED,6EA4EC;YAGD,cAQC;YARD,qGAQC;;;AAyFE,mBAAmB;IA3N/B,aAAa,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;GA2N3C,mBAAmB,CAkH/B;;iFAlHY,mBAAmB;cA1N/B,SAAS;6BACI,KAAK,YACL,gBAAgB,YAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAgIL;;kBAwFJ,SAAS;mBAAC,QAAQ;;kFADV,mBAAmB","sourcesContent":["import { Component, ChangeDetectorRef, ElementRef, ViewChild, AfterViewInit } from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\n\n/**\n * Runtime renderer for WebURL dashboard parts.\n * Displays web content in an iframe with configurable sandbox permissions.\n */\n@RegisterClass(BaseDashboardPart, 'WebURLPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-weburl-part',\n template: `\n <div class=\"weburl-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading...</span>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage) {\n <mj-empty-state\n class=\"part-placeholder\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load content\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\">\n @if (rawUrl) {\n <a actions [href]=\"rawUrl\" target=\"_blank\" class=\"open-link\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in new window\n </a>\n }\n </mj-empty-state>\n }\n\n <!-- No URL configured -->\n @if (!IsLoading && !ErrorMessage && !SafeUrl) {\n <mj-empty-state\n class=\"part-placeholder\"\n Icon=\"fa-solid fa-globe\"\n Title=\"No URL Configured\"\n Message=\"Click the configure button to set a URL for this part.\"\n Size=\"compact\" />\n }\n \n <!-- Iframe container - sandbox and allowfullscreen must be static, so we use ng-container to switch -->\n @if (!IsLoading && !ErrorMessage && SafeUrl) {\n <!-- Standard sandbox + fullscreen enabled -->\n @if (sandboxMode === 'standard' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Standard sandbox + fullscreen disabled -->\n @if (sandboxMode === 'standard' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen enabled -->\n @if (sandboxMode === 'strict' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Strict sandbox + fullscreen disabled -->\n @if (sandboxMode === 'strict' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen enabled -->\n @if (sandboxMode === 'permissive' && allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n allowfullscreen\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n <!-- Permissive sandbox + fullscreen disabled -->\n @if (sandboxMode === 'permissive' && !allowFullscreen) {\n <iframe\n #iframe\n [src]=\"SafeUrl\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation\"\n [title]=\"Panel?.title || 'Embedded content'\"\n (load)=\"onIframeLoad()\"\n (error)=\"onIframeError($event)\"\n class=\"content-iframe\">\n </iframe>\n }\n }\n \n <!-- Fallback link shown below iframe if content might be blocked -->\n @if (!IsLoading && !ErrorMessage && SafeUrl && showFallbackLink) {\n <div class=\"iframe-fallback\">\n <span>If the content doesn't load:</span>\n <a [href]=\"rawUrl\" target=\"_blank\">\n <i class=\"fa-solid fa-external-link-alt\"></i>\n Open in new window\n </a>\n </div>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .weburl-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n position: relative; /* Required for overlay positioning */\n }\n\n .loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .loading-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .part-placeholder {\n height: 100%;\n }\n\n /* Projected \"open in new window\" link inside the error empty-state */\n .open-link {\n padding: 8px 16px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n text-decoration: none;\n border-radius: 4px;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n }\n\n .open-link:hover {\n background: var(--mj-brand-primary-hover);\n }\n\n .content-iframe {\n width: 100%;\n height: 100%;\n border: none;\n flex: 1;\n }\n\n .iframe-fallback {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n }\n\n .iframe-fallback a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .iframe-fallback a:hover {\n text-decoration: underline;\n }\n `]\n})\nexport class WebURLPartComponent extends BaseDashboardPart implements AfterViewInit {\n @ViewChild('iframe') iframeRef!: ElementRef<HTMLIFrameElement>;\n\n public SafeUrl: SafeResourceUrl | null = null;\n public rawUrl: string = '';\n public sandboxMode: 'standard' | 'strict' | 'permissive' = 'standard';\n public allowFullscreen: boolean = true;\n public showFallbackLink: boolean = false; // Hidden by default, shown if content might be blocked\n\n private sanitizer: DomSanitizer;\n private loadCheckTimeout: ReturnType<typeof setTimeout> | null = null;\n private iframeLoaded: boolean = false;\n\n constructor(cdr: ChangeDetectorRef, sanitizer: DomSanitizer) {\n super(cdr);\n this.sanitizer = sanitizer;\n }\n\n ngAfterViewInit(): void {\n // Initial load if panel is already set\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const url = config?.['url'] as string | undefined;\n\n // Clear any existing timeout\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // Reset state\n this.iframeLoaded = false;\n this.showFallbackLink = false;\n\n if (!url) {\n this.SafeUrl = null;\n this.rawUrl = '';\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n // Store raw URL for fallback link\n this.rawUrl = url;\n\n // Sanitize and set URL for iframe src binding\n this.SafeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);\n\n // Set sandbox mode (used by template to select correct iframe)\n this.sandboxMode = (config?.['sandboxMode'] as 'standard' | 'strict' | 'permissive') || 'standard';\n\n // Set fullscreen permission\n this.allowFullscreen = (config?.['allowFullscreen'] as boolean) ?? true;\n\n this.setLoading(false);\n\n // Set up a delayed check to detect if embedding might be blocked\n // If after 3 seconds the iframe hasn't successfully loaded content we can access,\n // show the blocked overlay. This is a heuristic since we can't detect X-Frame-Options directly.\n this.loadCheckTimeout = setTimeout(() => {\n this.checkIfBlocked();\n }, 3000);\n\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load URL');\n }\n }\n\n /**\n * Check if the iframe content might be blocked.\n * Called after a timeout - if the iframe load event hasn't fired, show the fallback link.\n */\n private checkIfBlocked(): void {\n // If the load event hasn't fired after the timeout, content might be blocked\n if (!this.iframeLoaded) {\n this.showFallbackLink = true;\n this.cdr.detectChanges();\n }\n // If iframeLoaded is true, content loaded successfully - keep fallback hidden\n }\n\n /**\n * Handle iframe load event\n */\n public onIframeLoad(): void {\n // Mark that the load event fired\n this.iframeLoaded = true;\n\n // Clear the timeout since the iframe loaded\n if (this.loadCheckTimeout) {\n clearTimeout(this.loadCheckTimeout);\n this.loadCheckTimeout = null;\n }\n\n // The iframe loaded something - could still be blocked content\n // We can't reliably detect X-Frame-Options blocking from JS\n // The fallback link provides a way for users to open the site if it doesn't load\n this.cdr.detectChanges();\n }\n\n /**\n * Handle iframe error event\n */\n public onIframeError(_event: Event): void {\n // This may fire for some load failures, but not for X-Frame-Options\n this.setError('This site cannot be embedded. It may block iframe embedding for security reasons.');\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-dashboard-viewer",
3
- "version": "5.43.0",
3
+ "version": "5.44.0",
4
4
  "description": "MemberJunction: Angular components for metadata-driven dashboards with Golden Layout panels, supporting views, queries, artifacts, and custom content",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -26,6 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@angular/compiler": "21.1.3",
28
28
  "@angular/compiler-cli": "21.1.3",
29
+ "@memberjunction/ng-test-utils": "5.44.0",
29
30
  "vitest": "^4.0.18"
30
31
  },
31
32
  "peerDependencies": {
@@ -37,16 +38,17 @@
37
38
  "golden-layout": "^2.6.0"
38
39
  },
39
40
  "dependencies": {
40
- "@memberjunction/core": "5.43.0",
41
- "@memberjunction/core-entities": "5.43.0",
42
- "@memberjunction/global": "5.43.0",
43
- "@memberjunction/ng-artifacts": "5.43.0",
44
- "@memberjunction/ng-base-types": "5.43.0",
45
- "@memberjunction/ng-entity-viewer": "5.43.0",
46
- "@memberjunction/ng-map-view": "5.43.0",
47
- "@memberjunction/ng-query-viewer": "5.43.0",
48
- "@memberjunction/ng-shared-generic": "5.43.0",
49
- "@memberjunction/ng-trees": "5.43.0",
41
+ "@memberjunction/ng-ui-components": "5.44.0",
42
+ "@memberjunction/core": "5.44.0",
43
+ "@memberjunction/core-entities": "5.44.0",
44
+ "@memberjunction/global": "5.44.0",
45
+ "@memberjunction/ng-artifacts": "5.44.0",
46
+ "@memberjunction/ng-base-types": "5.44.0",
47
+ "@memberjunction/ng-entity-viewer": "5.44.0",
48
+ "@memberjunction/ng-map-view": "5.44.0",
49
+ "@memberjunction/ng-query-viewer": "5.44.0",
50
+ "@memberjunction/ng-shared-generic": "5.44.0",
51
+ "@memberjunction/ng-trees": "5.44.0",
50
52
  "rxjs": "^7.8.2",
51
53
  "tslib": "^2.8.1"
52
54
  },
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=exports.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"exports.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/exports.test.ts"],"names":[],"mappings":""}
@@ -1,17 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { existsSync } from 'fs';
3
- import { resolve } from 'path';
4
- describe('@memberjunction/ng-dashboard-viewer', () => {
5
- it('should have a public API entry point', () => {
6
- const pkgRoot = resolve(__dirname, '../..');
7
- const hasPublicApi = existsSync(resolve(pkgRoot, 'src/public-api.ts'));
8
- const hasIndex = existsSync(resolve(pkgRoot, 'src/index.ts'));
9
- expect(hasPublicApi || hasIndex).toBe(true);
10
- });
11
- it('should have a package.json with correct name', () => {
12
- const pkgRoot = resolve(__dirname, '../..');
13
- const pkg = require(resolve(pkgRoot, 'package.json'));
14
- expect(pkg.name).toBe('@memberjunction/ng-dashboard-viewer');
15
- });
16
- });
17
- //# sourceMappingURL=exports.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"exports.test.js","sourceRoot":"","sources":["../../src/__tests__/exports.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QAC9D,MAAM,CAAC,YAAY,IAAI,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport { existsSync } from 'fs';\nimport { resolve } from 'path';\n\ndescribe('@memberjunction/ng-dashboard-viewer', () => {\n it('should have a public API entry point', () => {\n const pkgRoot = resolve(__dirname, '../..');\n const hasPublicApi = existsSync(resolve(pkgRoot, 'src/public-api.ts'));\n const hasIndex = existsSync(resolve(pkgRoot, 'src/index.ts'));\n expect(hasPublicApi || hasIndex).toBe(true);\n });\n\n it('should have a package.json with correct name', () => {\n const pkgRoot = resolve(__dirname, '../..');\n const pkg = require(resolve(pkgRoot, 'package.json'));\n expect(pkg.name).toBe('@memberjunction/ng-dashboard-viewer');\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
@@ -1,7 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- describe('@memberjunction/ng-dashboard-viewer', () => {
3
- it('should have a passing test', () => {
4
- expect(true).toBe(true);
5
- });
6
- });
7
- //# sourceMappingURL=index.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it, expect } from 'vitest';\n\ndescribe('@memberjunction/ng-dashboard-viewer', () => {\n it('should have a passing test', () => {\n expect(true).toBe(true);\n });\n});\n"]}