@memberjunction/ng-dashboard-viewer 6.1.4 → 6.2.0-edge.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/lib/config-dialogs/confirm-dialog.component.d.ts +58 -8
  2. package/dist/lib/config-dialogs/confirm-dialog.component.d.ts.map +1 -1
  3. package/dist/lib/config-dialogs/confirm-dialog.component.js +119 -17
  4. package/dist/lib/config-dialogs/confirm-dialog.component.js.map +1 -1
  5. package/dist/lib/config-panels/artifact-config-panel.component.d.ts +63 -11
  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 +152 -48
  8. package/dist/lib/config-panels/artifact-config-panel.component.js.map +1 -1
  9. package/dist/lib/config-panels/query-config-panel.component.d.ts +57 -9
  10. package/dist/lib/config-panels/query-config-panel.component.d.ts.map +1 -1
  11. package/dist/lib/config-panels/query-config-panel.component.js +142 -46
  12. package/dist/lib/config-panels/query-config-panel.component.js.map +1 -1
  13. package/dist/lib/config-panels/view-config-panel.component.d.ts +69 -11
  14. package/dist/lib/config-panels/view-config-panel.component.d.ts.map +1 -1
  15. package/dist/lib/config-panels/view-config-panel.component.js +165 -49
  16. package/dist/lib/config-panels/view-config-panel.component.js.map +1 -1
  17. package/dist/lib/config-panels/weburl-config-panel.component.d.ts +42 -6
  18. package/dist/lib/config-panels/weburl-config-panel.component.d.ts.map +1 -1
  19. package/dist/lib/config-panels/weburl-config-panel.component.js +113 -41
  20. package/dist/lib/config-panels/weburl-config-panel.component.js.map +1 -1
  21. package/dist/lib/dashboard-browser/dashboard-browser.component.d.ts.map +1 -1
  22. package/dist/lib/dashboard-browser/dashboard-browser.component.js +7 -4
  23. package/dist/lib/dashboard-browser/dashboard-browser.component.js.map +1 -1
  24. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts +163 -12
  25. package/dist/lib/dashboard-viewer/dashboard-viewer.component.d.ts.map +1 -1
  26. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js +332 -78
  27. package/dist/lib/dashboard-viewer/dashboard-viewer.component.js.map +1 -1
  28. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.d.ts +63 -9
  29. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.d.ts.map +1 -1
  30. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js +172 -66
  31. package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js.map +1 -1
  32. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.d.ts +11 -1
  33. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.d.ts.map +1 -1
  34. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js +30 -10
  35. package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js.map +1 -1
  36. package/dist/lib/models/dashboard-types.d.ts +8 -0
  37. package/dist/lib/models/dashboard-types.d.ts.map +1 -1
  38. package/dist/lib/models/dashboard-types.js +21 -5
  39. package/dist/lib/models/dashboard-types.js.map +1 -1
  40. package/dist/lib/parts/artifact-part.component.d.ts +63 -7
  41. package/dist/lib/parts/artifact-part.component.d.ts.map +1 -1
  42. package/dist/lib/parts/artifact-part.component.js +98 -22
  43. package/dist/lib/parts/artifact-part.component.js.map +1 -1
  44. package/dist/lib/parts/query-part.component.d.ts +26 -4
  45. package/dist/lib/parts/query-part.component.d.ts.map +1 -1
  46. package/dist/lib/parts/query-part.component.js +62 -18
  47. package/dist/lib/parts/query-part.component.js.map +1 -1
  48. package/dist/lib/parts/view-part.component.d.ts +21 -3
  49. package/dist/lib/parts/view-part.component.d.ts.map +1 -1
  50. package/dist/lib/parts/view-part.component.js +55 -19
  51. package/dist/lib/parts/view-part.component.js.map +1 -1
  52. package/dist/lib/parts/weburl-part.component.d.ts +29 -5
  53. package/dist/lib/parts/weburl-part.component.d.ts.map +1 -1
  54. package/dist/lib/parts/weburl-part.component.js +63 -15
  55. package/dist/lib/parts/weburl-part.component.js.map +1 -1
  56. package/dist/lib/services/golden-layout-wrapper.service.d.ts +65 -5
  57. package/dist/lib/services/golden-layout-wrapper.service.d.ts.map +1 -1
  58. package/dist/lib/services/golden-layout-wrapper.service.js +135 -27
  59. package/dist/lib/services/golden-layout-wrapper.service.js.map +1 -1
  60. package/package.json +13 -13
@@ -39,9 +39,33 @@ function ViewPartComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
39
39
  * Displays entity data using mj-entity-viewer with grid, cards, or timeline layout.
40
40
  */
41
41
  let ViewPartComponent = class ViewPartComponent extends BaseDashboardPart {
42
- hasView = false;
43
- viewEntity = null;
44
- entityInfo = null;
42
+ HasView = false;
43
+ /** @deprecated Use {@link HasView}. */
44
+ get hasView() {
45
+ return this.HasView;
46
+ }
47
+ /** @deprecated Use {@link HasView}. */
48
+ set hasView(value) {
49
+ this.HasView = value;
50
+ }
51
+ ViewEntity = null;
52
+ /** @deprecated Use {@link ViewEntity}. */
53
+ get viewEntity() {
54
+ return this.ViewEntity;
55
+ }
56
+ /** @deprecated Use {@link ViewEntity}. */
57
+ set viewEntity(value) {
58
+ this.ViewEntity = value;
59
+ }
60
+ EntityInfo = null;
61
+ /** @deprecated Use {@link EntityInfo}. */
62
+ get entityInfo() {
63
+ return this.EntityInfo;
64
+ }
65
+ /** @deprecated Use {@link EntityInfo}. */
66
+ set entityInfo(value) {
67
+ this.EntityInfo = value;
68
+ }
45
69
  constructor(cdr) {
46
70
  super(cdr);
47
71
  }
@@ -55,7 +79,7 @@ let ViewPartComponent = class ViewPartComponent extends BaseDashboardPart {
55
79
  const viewId = config?.['viewId'];
56
80
  const entityName = config?.['entityName'];
57
81
  if (!viewId && !entityName) {
58
- this.hasView = false;
82
+ this.HasView = false;
59
83
  this.cdr.detectChanges();
60
84
  return;
61
85
  }
@@ -66,43 +90,43 @@ let ViewPartComponent = class ViewPartComponent extends BaseDashboardPart {
66
90
  // Load saved view by ID
67
91
  const viewEntity = await p.GetEntityObject('MJ: User Views', p.CurrentUser);
68
92
  const loaded = await viewEntity.Load(viewId);
69
- this.viewEntity = viewEntity; // IMPORTANT - only set this.viewEntity AFTER we have it loaded in the above
93
+ this.ViewEntity = viewEntity; // IMPORTANT - only set this.viewEntity AFTER we have it loaded in the above
70
94
  if (!loaded) {
71
95
  throw new Error('View not found');
72
96
  }
73
97
  // Get entity info from the view - prefer ViewEntityInfo if available (set by MJUserViewEntityExtended.Load)
74
98
  // Fall back to looking up by Entity name (virtual field) or EntityID
75
99
  if (viewEntity.ViewEntityInfo) {
76
- this.entityInfo = viewEntity.ViewEntityInfo;
100
+ this.EntityInfo = viewEntity.ViewEntityInfo;
77
101
  }
78
102
  else if (viewEntity.Entity) {
79
- this.entityInfo = p.EntityByName(viewEntity.Entity) || null;
103
+ this.EntityInfo = p.EntityByName(viewEntity.Entity) || null;
80
104
  }
81
105
  else if (viewEntity.EntityID) {
82
106
  // Last resort: look up by EntityID
83
- this.entityInfo = p.Entities.find(e => UUIDsEqual(e.ID, viewEntity.EntityID)) || null;
107
+ this.EntityInfo = p.Entities.find(e => UUIDsEqual(e.ID, viewEntity.EntityID)) || null;
84
108
  }
85
- if (!this.entityInfo) {
86
- throw new Error(`Could not determine entity for view "${this.viewEntity.Name}" (ID: ${viewId})`);
109
+ if (!this.EntityInfo) {
110
+ throw new Error(`Could not determine entity for view "${this.ViewEntity.Name}" (ID: ${viewId})`);
87
111
  }
88
112
  }
89
113
  else if (entityName) {
90
114
  // Create dynamic view for entity (no saved view)
91
- this.entityInfo = p.EntityByName(entityName) || null;
92
- if (!this.entityInfo) {
115
+ this.EntityInfo = p.EntityByName(entityName) || null;
116
+ if (!this.EntityInfo) {
93
117
  throw new Error(`Entity "${entityName}" not found`);
94
118
  }
95
119
  // No viewEntity means the entity-viewer will show all records
96
- this.viewEntity = null;
120
+ this.ViewEntity = null;
97
121
  }
98
- this.hasView = true;
122
+ this.HasView = true;
99
123
  this.setLoading(false);
100
124
  }
101
125
  catch (error) {
102
126
  this.setError(error instanceof Error ? error.message : 'Failed to load view');
103
127
  }
104
128
  }
105
- onRecordSelected(event) {
129
+ OnRecordSelected(event) {
106
130
  // Emit data change event with selected record
107
131
  this.emitDataChanged({
108
132
  type: 'record-selected',
@@ -110,7 +134,11 @@ let ViewPartComponent = class ViewPartComponent extends BaseDashboardPart {
110
134
  primaryKey: event.record?.PrimaryKey
111
135
  });
112
136
  }
113
- onRecordOpened(event) {
137
+ /** @deprecated Use {@link OnRecordSelected}. */
138
+ onRecordSelected(event) {
139
+ return this.OnRecordSelected(event);
140
+ }
141
+ OnRecordOpened(event) {
114
142
  // Emit data change event for record open (for any listeners that need it)
115
143
  this.emitDataChanged({
116
144
  type: 'record-opened',
@@ -122,6 +150,10 @@ let ViewPartComponent = class ViewPartComponent extends BaseDashboardPart {
122
150
  this.RequestOpenEntityRecord(event.entity.Name, event.compositeKey.ToURLSegment(), 'view', false);
123
151
  }
124
152
  }
153
+ /** @deprecated Use {@link OnRecordOpened}. */
154
+ onRecordOpened(event) {
155
+ return this.OnRecordOpened(event);
156
+ }
125
157
  /**
126
158
  * Handle a plug-in renderer's request (bubbled up via the inner entity-viewer) to open a
127
159
  * *related* record on a (possibly different) entity — e.g. a grid foreign-key drill-through.
@@ -129,15 +161,19 @@ let ViewPartComponent = class ViewPartComponent extends BaseDashboardPart {
129
161
  *
130
162
  * @param nav the related-record navigation payload: the target entity name and the record's key.
131
163
  */
132
- onOpenRelatedRecordRequested(nav) {
164
+ OnOpenRelatedRecordRequested(nav) {
133
165
  if (nav?.entityName && nav.recordKey != null) {
134
166
  this.RequestOpenEntityRecord(nav.entityName, String(nav.recordKey), 'view', false);
135
167
  }
136
168
  }
169
+ /** @deprecated Use {@link OnOpenRelatedRecordRequested}. */
170
+ onOpenRelatedRecordRequested(nav) {
171
+ return this.OnOpenRelatedRecordRequested(nav);
172
+ }
137
173
  cleanup() {
138
174
  // EntityViewer handles its own cleanup
139
- this.viewEntity = null;
140
- this.entityInfo = null;
175
+ this.ViewEntity = null;
176
+ this.EntityInfo = null;
141
177
  }
142
178
  static ɵfac = function ViewPartComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ViewPartComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
143
179
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ViewPartComponent, selectors: [["mj-view-part"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 8, consts: [[1, "view-part"], [1, "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Couldn't load view", "Size", "compact", 1, "part-placeholder", 3, "Message"], ["Icon", "fa-solid fa-table", "Title", "No View Selected", "Message", "Click the configure button to select a view for this part.", "Size", "compact", 1, "part-placeholder"], [3, "Entity", "ViewEntity"], ["text", "Loading view..."], [3, "RecordSelected", "RecordOpened", "OpenRelatedRecordRequested", "Entity", "ViewEntity"]], template: function ViewPartComponent_Template(rf, ctx) { if (rf & 1) {
@@ -1 +1 @@
1
- {"version":3,"file":"view-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/view-part.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectorRef, AfterViewInit, OnDestroy } from '@angular/core';\nimport { RegisterClass, UUIDsEqual } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\nimport { EntityInfo } from '@memberjunction/core';\nimport { MJUserViewEntityExtended } from '@memberjunction/core-entities';\nimport { RecordSelectedEvent, RecordOpenedEvent, ViewRelatedRecordNavigation } from '@memberjunction/ng-entity-viewer';\n\n/**\n * Runtime renderer for View dashboard parts.\n * Displays entity data using mj-entity-viewer with grid, cards, or timeline layout.\n */\n@RegisterClass(BaseDashboardPart, 'ViewPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-view-part',\n template: `\n <div class=\"view-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading view...\"></mj-loading>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage && !IsLoading) {\n <mj-empty-state\n class=\"part-placeholder\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load view\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\" />\n }\n\n <!-- No view configured -->\n @if (!IsLoading && !ErrorMessage && !hasView) {\n <mj-empty-state\n class=\"part-placeholder\"\n Icon=\"fa-solid fa-table\"\n Title=\"No View Selected\"\n Message=\"Click the configure button to select a view for this part.\"\n Size=\"compact\" />\n }\n \n <!-- Entity Viewer -->\n @if (!IsLoading && !ErrorMessage && hasView && entityInfo) {\n <mj-entity-viewer\n [Entity]=\"entityInfo\"\n [ViewEntity]=\"viewEntity\"\n (RecordSelected)=\"onRecordSelected($event)\"\n (RecordOpened)=\"onRecordOpened($event)\"\n (OpenRelatedRecordRequested)=\"onOpenRelatedRecordRequested($event)\">\n </mj-entity-viewer>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .view-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\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 .part-placeholder {\n height: 100%;\n }\n\n mj-entity-viewer {\n flex: 1;\n min-height: 0;\n }\n `]\n})\nexport class ViewPartComponent extends BaseDashboardPart implements AfterViewInit, OnDestroy {\n public hasView = false;\n public viewEntity: MJUserViewEntityExtended | null = null;\n public entityInfo: EntityInfo | null = null;\n\n constructor(cdr: ChangeDetectorRef) {\n super(cdr);\n }\n\n ngAfterViewInit(): void {\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const viewId = config?.['viewId'] as string | undefined;\n const entityName = config?.['entityName'] as string | undefined;\n\n if (!viewId && !entityName) {\n this.hasView = false;\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n const p = this.ProviderToUse;\n\n if (viewId) {\n // Load saved view by ID\n const viewEntity = await p.GetEntityObject<MJUserViewEntityExtended>('MJ: User Views', p.CurrentUser);\n const loaded = await viewEntity.Load(viewId);\n this.viewEntity = viewEntity; // IMPORTANT - only set this.viewEntity AFTER we have it loaded in the above\n\n if (!loaded) {\n throw new Error('View not found');\n }\n\n // Get entity info from the view - prefer ViewEntityInfo if available (set by MJUserViewEntityExtended.Load)\n // Fall back to looking up by Entity name (virtual field) or EntityID\n if (viewEntity.ViewEntityInfo) {\n this.entityInfo = viewEntity.ViewEntityInfo;\n } else if (viewEntity.Entity) {\n this.entityInfo = p.EntityByName(viewEntity!.Entity) || null;\n } else if (viewEntity.EntityID) {\n // Last resort: look up by EntityID\n this.entityInfo = p.Entities.find(e => UUIDsEqual(e.ID, viewEntity!.EntityID)) || null;\n }\n\n if (!this.entityInfo) {\n throw new Error(`Could not determine entity for view \"${this.viewEntity.Name}\" (ID: ${viewId})`);\n }\n } else if (entityName) {\n // Create dynamic view for entity (no saved view)\n this.entityInfo = p.EntityByName(entityName) || null;\n\n if (!this.entityInfo) {\n throw new Error(`Entity \"${entityName}\" not found`);\n }\n\n // No viewEntity means the entity-viewer will show all records\n this.viewEntity = null;\n }\n\n this.hasView = true;\n this.setLoading(false);\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load view');\n }\n }\n\n public onRecordSelected(event: RecordSelectedEvent): void {\n // Emit data change event with selected record\n this.emitDataChanged({\n type: 'record-selected',\n record: event.record,\n primaryKey: event.record?.PrimaryKey\n });\n }\n\n public onRecordOpened(event: RecordOpenedEvent): void {\n // Emit data change event for record open (for any listeners that need it)\n this.emitDataChanged({\n type: 'record-opened',\n record: event.record,\n primaryKey: event.record?.PrimaryKey\n });\n\n // Request navigation to open the record\n if (event.entity && event.compositeKey) {\n this.RequestOpenEntityRecord(\n event.entity.Name,\n event.compositeKey.ToURLSegment(),\n 'view',\n false\n );\n }\n }\n\n /**\n * Handle a plug-in renderer's request (bubbled up via the inner entity-viewer) to open a\n * *related* record on a (possibly different) entity — e.g. a grid foreign-key drill-through.\n * Requests navigation through the dashboard-part routing contract.\n *\n * @param nav the related-record navigation payload: the target entity name and the record's key.\n */\n public onOpenRelatedRecordRequested(nav: ViewRelatedRecordNavigation): void {\n if (nav?.entityName && nav.recordKey != null) {\n this.RequestOpenEntityRecord(\n nav.entityName,\n String(nav.recordKey),\n 'view',\n false\n );\n }\n }\n\n protected override cleanup(): void {\n // EntityViewer handles its own cleanup\n this.viewEntity = null;\n this.entityInfo = null;\n }\n}\n"]}
1
+ {"version":3,"file":"view-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/view-part.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, ChangeDetectorRef, AfterViewInit, OnDestroy } from '@angular/core';\nimport { RegisterClass, UUIDsEqual } from '@memberjunction/global';\nimport { BaseDashboardPart } from './base-dashboard-part';\nimport { PanelConfig } from '../models/dashboard-types';\nimport { EntityInfo } from '@memberjunction/core';\nimport { MJUserViewEntityExtended } from '@memberjunction/core-entities';\nimport { RecordSelectedEvent, RecordOpenedEvent, ViewRelatedRecordNavigation } from '@memberjunction/ng-entity-viewer';\n\n/**\n * Runtime renderer for View dashboard parts.\n * Displays entity data using mj-entity-viewer with grid, cards, or timeline layout.\n */\n@RegisterClass(BaseDashboardPart, 'ViewPanelRenderer')\n@Component({\n standalone: false,\n selector: 'mj-view-part',\n template: `\n <div class=\"view-part\" [class.loading]=\"IsLoading\" [class.error]=\"ErrorMessage\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading view...\"></mj-loading>\n </div>\n }\n \n <!-- Error state -->\n @if (ErrorMessage && !IsLoading) {\n <mj-empty-state\n class=\"part-placeholder\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Couldn't load view\"\n [Message]=\"ErrorMessage\"\n Size=\"compact\" />\n }\n\n <!-- No view configured -->\n @if (!IsLoading && !ErrorMessage && !hasView) {\n <mj-empty-state\n class=\"part-placeholder\"\n Icon=\"fa-solid fa-table\"\n Title=\"No View Selected\"\n Message=\"Click the configure button to select a view for this part.\"\n Size=\"compact\" />\n }\n \n <!-- Entity Viewer -->\n @if (!IsLoading && !ErrorMessage && hasView && entityInfo) {\n <mj-entity-viewer\n [Entity]=\"entityInfo\"\n [ViewEntity]=\"viewEntity\"\n (RecordSelected)=\"onRecordSelected($event)\"\n (RecordOpened)=\"onRecordOpened($event)\"\n (OpenRelatedRecordRequested)=\"onOpenRelatedRecordRequested($event)\">\n </mj-entity-viewer>\n }\n </div>\n `,\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .view-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\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 .part-placeholder {\n height: 100%;\n }\n\n mj-entity-viewer {\n flex: 1;\n min-height: 0;\n }\n `]\n})\nexport class ViewPartComponent extends BaseDashboardPart implements AfterViewInit, OnDestroy {\n public HasView = false;\n\n /** @deprecated Use {@link HasView}. */\n public get hasView() {\n return this.HasView;\n }\n /** @deprecated Use {@link HasView}. */\n public set hasView(value) {\n this.HasView = value;\n }\n public ViewEntity: MJUserViewEntityExtended | null = null;\n\n /** @deprecated Use {@link ViewEntity}. */\n public get viewEntity(): MJUserViewEntityExtended | null {\n return this.ViewEntity;\n }\n /** @deprecated Use {@link ViewEntity}. */\n public set viewEntity(value: MJUserViewEntityExtended | null) {\n this.ViewEntity = value;\n }\n public EntityInfo: EntityInfo | null = null;\n\n /** @deprecated Use {@link EntityInfo}. */\n public get entityInfo(): EntityInfo | null {\n return this.EntityInfo;\n }\n /** @deprecated Use {@link EntityInfo}. */\n public set entityInfo(value: EntityInfo | null) {\n this.EntityInfo = value;\n }\n\n constructor(cdr: ChangeDetectorRef) {\n super(cdr);\n }\n\n ngAfterViewInit(): void {\n if (this.Panel) {\n this.loadContent();\n }\n }\n\n public async loadContent(): Promise<void> {\n const config = this.getConfig<PanelConfig>();\n const viewId = config?.['viewId'] as string | undefined;\n const entityName = config?.['entityName'] as string | undefined;\n\n if (!viewId && !entityName) {\n this.HasView = false;\n this.cdr.detectChanges();\n return;\n }\n\n this.setLoading(true);\n\n try {\n const p = this.ProviderToUse;\n\n if (viewId) {\n // Load saved view by ID\n const viewEntity = await p.GetEntityObject<MJUserViewEntityExtended>('MJ: User Views', p.CurrentUser);\n const loaded = await viewEntity.Load(viewId);\n this.ViewEntity = viewEntity; // IMPORTANT - only set this.viewEntity AFTER we have it loaded in the above\n\n if (!loaded) {\n throw new Error('View not found');\n }\n\n // Get entity info from the view - prefer ViewEntityInfo if available (set by MJUserViewEntityExtended.Load)\n // Fall back to looking up by Entity name (virtual field) or EntityID\n if (viewEntity.ViewEntityInfo) {\n this.EntityInfo = viewEntity.ViewEntityInfo;\n } else if (viewEntity.Entity) {\n this.EntityInfo = p.EntityByName(viewEntity!.Entity) || null;\n } else if (viewEntity.EntityID) {\n // Last resort: look up by EntityID\n this.EntityInfo = p.Entities.find(e => UUIDsEqual(e.ID, viewEntity!.EntityID)) || null;\n }\n\n if (!this.EntityInfo) {\n throw new Error(`Could not determine entity for view \"${this.ViewEntity.Name}\" (ID: ${viewId})`);\n }\n } else if (entityName) {\n // Create dynamic view for entity (no saved view)\n this.EntityInfo = p.EntityByName(entityName) || null;\n\n if (!this.EntityInfo) {\n throw new Error(`Entity \"${entityName}\" not found`);\n }\n\n // No viewEntity means the entity-viewer will show all records\n this.ViewEntity = null;\n }\n\n this.HasView = true;\n this.setLoading(false);\n } catch (error) {\n this.setError(error instanceof Error ? error.message : 'Failed to load view');\n }\n }\n\n public OnRecordSelected(event: RecordSelectedEvent): void {\n // Emit data change event with selected record\n this.emitDataChanged({\n type: 'record-selected',\n record: event.record,\n primaryKey: event.record?.PrimaryKey\n });\n }\n\n /** @deprecated Use {@link OnRecordSelected}. */\n public onRecordSelected(event: RecordSelectedEvent): void {\n return this.OnRecordSelected(event);\n }\n\n public OnRecordOpened(event: RecordOpenedEvent): void {\n // Emit data change event for record open (for any listeners that need it)\n this.emitDataChanged({\n type: 'record-opened',\n record: event.record,\n primaryKey: event.record?.PrimaryKey\n });\n\n // Request navigation to open the record\n if (event.entity && event.compositeKey) {\n this.RequestOpenEntityRecord(\n event.entity.Name,\n event.compositeKey.ToURLSegment(),\n 'view',\n false\n );\n }\n }\n\n /** @deprecated Use {@link OnRecordOpened}. */\n public onRecordOpened(event: RecordOpenedEvent): void {\n return this.OnRecordOpened(event);\n }\n\n /**\n * Handle a plug-in renderer's request (bubbled up via the inner entity-viewer) to open a\n * *related* record on a (possibly different) entity — e.g. a grid foreign-key drill-through.\n * Requests navigation through the dashboard-part routing contract.\n *\n * @param nav the related-record navigation payload: the target entity name and the record's key.\n */\n public OnOpenRelatedRecordRequested(nav: ViewRelatedRecordNavigation): void {\n if (nav?.entityName && nav.recordKey != null) {\n this.RequestOpenEntityRecord(\n nav.entityName,\n String(nav.recordKey),\n 'view',\n false\n );\n }\n }\n\n /** @deprecated Use {@link OnOpenRelatedRecordRequested}. */\n public onOpenRelatedRecordRequested(nav: ViewRelatedRecordNavigation): void {\n return this.OnOpenRelatedRecordRequested(nav);\n }\n\n protected override cleanup(): void {\n // EntityViewer handles its own cleanup\n this.ViewEntity = null;\n this.EntityInfo = null;\n }\n}\n"]}
@@ -7,12 +7,32 @@ import * as i0 from "@angular/core";
7
7
  * Displays web content in an iframe with configurable sandbox permissions.
8
8
  */
9
9
  export declare class WebURLPartComponent extends BaseDashboardPart implements AfterViewInit {
10
- iframeRef: ElementRef<HTMLIFrameElement>;
10
+ IframeRef: ElementRef<HTMLIFrameElement>;
11
+ /** @deprecated Use {@link IframeRef}. */
12
+ get iframeRef(): ElementRef<HTMLIFrameElement>;
13
+ /** @deprecated Use {@link IframeRef}. */
14
+ set iframeRef(value: ElementRef<HTMLIFrameElement>);
11
15
  SafeUrl: SafeResourceUrl | null;
12
- rawUrl: string;
13
- sandboxMode: 'standard' | 'strict' | 'permissive';
14
- allowFullscreen: boolean;
15
- showFallbackLink: boolean;
16
+ RawUrl: string;
17
+ /** @deprecated Use {@link RawUrl}. */
18
+ get rawUrl(): string;
19
+ /** @deprecated Use {@link RawUrl}. */
20
+ set rawUrl(value: string);
21
+ SandboxMode: 'standard' | 'strict' | 'permissive';
22
+ /** @deprecated Use {@link SandboxMode}. */
23
+ get sandboxMode(): 'standard' | 'strict' | 'permissive';
24
+ /** @deprecated Use {@link SandboxMode}. */
25
+ set sandboxMode(value: 'standard' | 'strict' | 'permissive');
26
+ AllowFullscreen: boolean;
27
+ /** @deprecated Use {@link AllowFullscreen}. */
28
+ get allowFullscreen(): boolean;
29
+ /** @deprecated Use {@link AllowFullscreen}. */
30
+ set allowFullscreen(value: boolean);
31
+ ShowFallbackLink: boolean;
32
+ /** @deprecated Use {@link ShowFallbackLink}. */
33
+ get showFallbackLink(): boolean;
34
+ /** @deprecated Use {@link ShowFallbackLink}. */
35
+ set showFallbackLink(value: boolean);
16
36
  private sanitizer;
17
37
  private loadCheckTimeout;
18
38
  private iframeLoaded;
@@ -27,10 +47,14 @@ export declare class WebURLPartComponent extends BaseDashboardPart implements Af
27
47
  /**
28
48
  * Handle iframe load event
29
49
  */
50
+ OnIframeLoad(): void;
51
+ /** @deprecated Use {@link OnIframeLoad}. */
30
52
  onIframeLoad(): void;
31
53
  /**
32
54
  * Handle iframe error event
33
55
  */
56
+ OnIframeError(_event: Event): void;
57
+ /** @deprecated Use {@link OnIframeError}. */
34
58
  onIframeError(_event: Event): void;
35
59
  static ɵfac: i0.ɵɵFactoryDeclaration<WebURLPartComponent, never>;
36
60
  static ɵcmp: i0.ɵɵComponentDeclaration<WebURLPartComponent, "mj-weburl-part", never, {}, {}, never, never, false, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"weburl-part.component.d.ts","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,UAAU,EAAa,aAAa,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;;AAG1D;;;GAGG;AACH,qBA2Na,mBAAoB,SAAQ,iBAAkB,YAAW,aAAa;IAC1D,SAAS,EAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAExD,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IACvC,MAAM,EAAE,MAAM,CAAM;IACpB,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAc;IAC/D,eAAe,EAAE,OAAO,CAAQ;IAChC,gBAAgB,EAAE,OAAO,CAAS;IAEzC,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,YAAY,CAAkB;gBAE1B,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAK3D,eAAe,IAAI,IAAI;IAOV,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDzC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACI,YAAY,IAAI,IAAI;IAgB3B;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;yCA9GhC,mBAAmB;2CAAnB,mBAAmB;CAkH/B"}
1
+ {"version":3,"file":"weburl-part.component.d.ts","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,iBAAiB,EAAE,UAAU,EAAa,aAAa,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;;AAG1D;;;GAGG;AACH,qBA2Na,mBAAoB,SAAQ,iBAAkB,YAAW,aAAa;IAC1D,SAAS,EAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE/D,yCAAyC;IACzC,IAAI,SAAS,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAE7C;IACD,yCAAyC;IACzC,IAAI,SAAS,CAAC,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,EAEjD;IAEM,OAAO,EAAE,eAAe,GAAG,IAAI,CAAQ;IACvC,MAAM,EAAE,MAAM,CAAM;IAE3B,sCAAsC;IACtC,IAAW,MAAM,IAAI,MAAM,CAE1B;IACD,sCAAsC;IACtC,IAAW,MAAM,CAAC,KAAK,EAAE,MAAM,EAE9B;IACM,WAAW,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAc;IAEtE,2CAA2C;IAC3C,IAAW,WAAW,IAAI,UAAU,GAAG,QAAQ,GAAG,YAAY,CAE7D;IACD,2CAA2C;IAC3C,IAAW,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,QAAQ,GAAG,YAAY,EAEjE;IACM,eAAe,EAAE,OAAO,CAAQ;IAEvC,+CAA+C;IAC/C,IAAW,eAAe,IAAI,OAAO,CAEpC;IACD,+CAA+C;IAC/C,IAAW,eAAe,CAAC,KAAK,EAAE,OAAO,EAExC;IACM,gBAAgB,EAAE,OAAO,CAAS;IAEzC,gDAAgD;IAChD,IAAW,gBAAgB,IAAI,OAAO,CAErC;IACD,gDAAgD;IAChD,IAAW,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAEzC;IAED,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,gBAAgB,CAA8C;IACtE,OAAO,CAAC,YAAY,CAAkB;gBAE1B,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,YAAY;IAK3D,eAAe,IAAI,IAAI;IAOV,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDzC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACI,YAAY,IAAI,IAAI;IAgB3B,4CAA4C;IACrC,YAAY,IAAI,IAAI;IAI3B;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;IAKzC,6CAA6C;IACtC,aAAa,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;yCAtKhC,mBAAmB;2CAAnB,mBAAmB;CAyK/B"}
@@ -133,12 +133,52 @@ function WebURLPartComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
133
133
  * Displays web content in an iframe with configurable sandbox permissions.
134
134
  */
135
135
  let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
136
- iframeRef;
136
+ IframeRef;
137
+ /** @deprecated Use {@link IframeRef}. */
138
+ get iframeRef() {
139
+ return this.IframeRef;
140
+ }
141
+ /** @deprecated Use {@link IframeRef}. */
142
+ set iframeRef(value) {
143
+ this.IframeRef = value;
144
+ }
137
145
  SafeUrl = null;
138
- rawUrl = '';
139
- sandboxMode = 'standard';
140
- allowFullscreen = true;
141
- showFallbackLink = false; // Hidden by default, shown if content might be blocked
146
+ RawUrl = '';
147
+ /** @deprecated Use {@link RawUrl}. */
148
+ get rawUrl() {
149
+ return this.RawUrl;
150
+ }
151
+ /** @deprecated Use {@link RawUrl}. */
152
+ set rawUrl(value) {
153
+ this.RawUrl = value;
154
+ }
155
+ SandboxMode = 'standard';
156
+ /** @deprecated Use {@link SandboxMode}. */
157
+ get sandboxMode() {
158
+ return this.SandboxMode;
159
+ }
160
+ /** @deprecated Use {@link SandboxMode}. */
161
+ set sandboxMode(value) {
162
+ this.SandboxMode = value;
163
+ }
164
+ AllowFullscreen = true;
165
+ /** @deprecated Use {@link AllowFullscreen}. */
166
+ get allowFullscreen() {
167
+ return this.AllowFullscreen;
168
+ }
169
+ /** @deprecated Use {@link AllowFullscreen}. */
170
+ set allowFullscreen(value) {
171
+ this.AllowFullscreen = value;
172
+ }
173
+ ShowFallbackLink = false;
174
+ /** @deprecated Use {@link ShowFallbackLink}. */
175
+ get showFallbackLink() {
176
+ return this.ShowFallbackLink;
177
+ }
178
+ /** @deprecated Use {@link ShowFallbackLink}. */
179
+ set showFallbackLink(value) {
180
+ this.ShowFallbackLink = value;
181
+ } // Hidden by default, shown if content might be blocked
142
182
  sanitizer;
143
183
  loadCheckTimeout = null;
144
184
  iframeLoaded = false;
@@ -162,23 +202,23 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
162
202
  }
163
203
  // Reset state
164
204
  this.iframeLoaded = false;
165
- this.showFallbackLink = false;
205
+ this.ShowFallbackLink = false;
166
206
  if (!url) {
167
207
  this.SafeUrl = null;
168
- this.rawUrl = '';
208
+ this.RawUrl = '';
169
209
  this.cdr.detectChanges();
170
210
  return;
171
211
  }
172
212
  this.setLoading(true);
173
213
  try {
174
214
  // Store raw URL for fallback link
175
- this.rawUrl = url;
215
+ this.RawUrl = url;
176
216
  // Sanitize and set URL for iframe src binding
177
217
  this.SafeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(url);
178
218
  // Set sandbox mode (used by template to select correct iframe)
179
- this.sandboxMode = config?.['sandboxMode'] || 'standard';
219
+ this.SandboxMode = config?.['sandboxMode'] || 'standard';
180
220
  // Set fullscreen permission
181
- this.allowFullscreen = config?.['allowFullscreen'] ?? true;
221
+ this.AllowFullscreen = config?.['allowFullscreen'] ?? true;
182
222
  this.setLoading(false);
183
223
  // Set up a delayed check to detect if embedding might be blocked
184
224
  // If after 3 seconds the iframe hasn't successfully loaded content we can access,
@@ -198,7 +238,7 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
198
238
  checkIfBlocked() {
199
239
  // If the load event hasn't fired after the timeout, content might be blocked
200
240
  if (!this.iframeLoaded) {
201
- this.showFallbackLink = true;
241
+ this.ShowFallbackLink = true;
202
242
  this.cdr.detectChanges();
203
243
  }
204
244
  // If iframeLoaded is true, content loaded successfully - keep fallback hidden
@@ -206,7 +246,7 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
206
246
  /**
207
247
  * Handle iframe load event
208
248
  */
209
- onIframeLoad() {
249
+ OnIframeLoad() {
210
250
  // Mark that the load event fired
211
251
  this.iframeLoaded = true;
212
252
  // Clear the timeout since the iframe loaded
@@ -219,19 +259,27 @@ let WebURLPartComponent = class WebURLPartComponent extends BaseDashboardPart {
219
259
  // The fallback link provides a way for users to open the site if it doesn't load
220
260
  this.cdr.detectChanges();
221
261
  }
262
+ /** @deprecated Use {@link OnIframeLoad}. */
263
+ onIframeLoad() {
264
+ return this.OnIframeLoad();
265
+ }
222
266
  /**
223
267
  * Handle iframe error event
224
268
  */
225
- onIframeError(_event) {
269
+ OnIframeError(_event) {
226
270
  // This may fire for some load failures, but not for X-Frame-Options
227
271
  this.setError('This site cannot be embedded. It may block iframe embedding for security reasons.');
228
272
  }
273
+ /** @deprecated Use {@link OnIframeError}. */
274
+ onIframeError(_event) {
275
+ return this.OnIframeError(_event);
276
+ }
229
277
  static ɵfac = function WebURLPartComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || WebURLPartComponent)(i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i1.DomSanitizer)); };
230
278
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: WebURLPartComponent, selectors: [["mj-weburl-part"]], viewQuery: function WebURLPartComponent_Query(rf, ctx) { if (rf & 1) {
231
279
  i0.ɵɵviewQuery(_c0, 5);
232
280
  } if (rf & 2) {
233
281
  let _t;
234
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.iframeRef = _t.first);
282
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.IframeRef = _t.first);
235
283
  } }, 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) {
236
284
  i0.ɵɵelementStart(0, "div", 1);
237
285
  i0.ɵɵconditionalCreate(1, WebURLPartComponent_Conditional_1_Template, 4, 0, "div", 2);
@@ -389,7 +437,7 @@ export { WebURLPartComponent };
389
437
  }
390
438
  </div>
391
439
  `, 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 "] }]
392
- }], () => [{ type: i0.ChangeDetectorRef }, { type: i1.DomSanitizer }], { iframeRef: [{
440
+ }], () => [{ type: i0.ChangeDetectorRef }, { type: i1.DomSanitizer }], { IframeRef: [{
393
441
  type: ViewChild,
394
442
  args: ['iframe']
395
443
  }] }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"weburl-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"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","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"]}
1
+ {"version":3,"file":"weburl-part.component.js","sourceRoot":"","sources":["../../../src/lib/parts/weburl-part.component.ts"],"names":[],"mappings":"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","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 /** @deprecated Use {@link IframeRef}. */\n get iframeRef(): ElementRef<HTMLIFrameElement> {\n return this.IframeRef;\n }\n /** @deprecated Use {@link IframeRef}. */\n set iframeRef(value: ElementRef<HTMLIFrameElement>) {\n this.IframeRef = value;\n }\n\n public SafeUrl: SafeResourceUrl | null = null;\n public RawUrl: string = '';\n\n /** @deprecated Use {@link RawUrl}. */\n public get rawUrl(): string {\n return this.RawUrl;\n }\n /** @deprecated Use {@link RawUrl}. */\n public set rawUrl(value: string) {\n this.RawUrl = value;\n }\n public SandboxMode: 'standard' | 'strict' | 'permissive' = 'standard';\n\n /** @deprecated Use {@link SandboxMode}. */\n public get sandboxMode(): 'standard' | 'strict' | 'permissive' {\n return this.SandboxMode;\n }\n /** @deprecated Use {@link SandboxMode}. */\n public set sandboxMode(value: 'standard' | 'strict' | 'permissive') {\n this.SandboxMode = value;\n }\n public AllowFullscreen: boolean = true;\n\n /** @deprecated Use {@link AllowFullscreen}. */\n public get allowFullscreen(): boolean {\n return this.AllowFullscreen;\n }\n /** @deprecated Use {@link AllowFullscreen}. */\n public set allowFullscreen(value: boolean) {\n this.AllowFullscreen = value;\n }\n public ShowFallbackLink: boolean = false;\n\n /** @deprecated Use {@link ShowFallbackLink}. */\n public get showFallbackLink(): boolean {\n return this.ShowFallbackLink;\n }\n /** @deprecated Use {@link ShowFallbackLink}. */\n public set showFallbackLink(value: boolean) {\n this.ShowFallbackLink = value;\n } // 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 /** @deprecated Use {@link OnIframeLoad}. */\n public onIframeLoad(): void {\n return this.OnIframeLoad();\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 /** @deprecated Use {@link OnIframeError}. */\n public onIframeError(_event: Event): void {\n return this.OnIframeError(_event);\n }\n}\n"]}