@dotglitch/ngx-common 1.0.2

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/README.md +24 -0
  2. package/components/dynamic-html/dynamic-html.component.d.ts +15 -0
  3. package/components/dynamic-html/dynamic-html.module.d.ts +10 -0
  4. package/components/dynamic-html/dynamic-html.service.d.ts +18 -0
  5. package/components/dynamic-html/types.d.ts +12 -0
  6. package/components/lazy-loader/lazy-loader.component.d.ts +146 -0
  7. package/components/lazy-loader/lazy-loader.module.d.ts +10 -0
  8. package/components/lazy-loader/lazy-loader.service.d.ts +71 -0
  9. package/components/lazy-loader/types.d.ts +142 -0
  10. package/components/menu/menu.component.d.ts +52 -0
  11. package/components/tooltip/tooltip.component.d.ts +35 -0
  12. package/directives/menu.directive.d.ts +27 -0
  13. package/directives/tooltip.directive.d.ts +26 -0
  14. package/directives/utils.d.ts +8 -0
  15. package/esm2020/components/dynamic-html/dynamic-html.component.mjs +43 -0
  16. package/esm2020/components/dynamic-html/dynamic-html.module.mjs +27 -0
  17. package/esm2020/components/dynamic-html/dynamic-html.service.mjs +66 -0
  18. package/esm2020/components/dynamic-html/types.mjs +7 -0
  19. package/esm2020/components/lazy-loader/lazy-loader.component.mjs +360 -0
  20. package/esm2020/components/lazy-loader/lazy-loader.module.mjs +29 -0
  21. package/esm2020/components/lazy-loader/lazy-loader.service.mjs +215 -0
  22. package/esm2020/components/lazy-loader/types.mjs +26 -0
  23. package/esm2020/components/menu/menu.component.mjs +316 -0
  24. package/esm2020/components/tooltip/tooltip.component.mjs +135 -0
  25. package/esm2020/directives/menu.directive.mjs +112 -0
  26. package/esm2020/directives/tooltip.directive.mjs +92 -0
  27. package/esm2020/directives/utils.mjs +120 -0
  28. package/esm2020/dotglitch-ngx-common.mjs +5 -0
  29. package/esm2020/pipes/html-bypass.pipe.mjs +27 -0
  30. package/esm2020/pipes/resource-bypass.pipe.mjs +27 -0
  31. package/esm2020/pipes/script-bypass.pipe.mjs +27 -0
  32. package/esm2020/pipes/style-bypass.pipe.mjs +27 -0
  33. package/esm2020/pipes/url-bypass.pipe.mjs +27 -0
  34. package/esm2020/public-api.mjs +39 -0
  35. package/esm2020/services/dependency.service.mjs +55 -0
  36. package/esm2020/services/dialog.service.mjs +66 -0
  37. package/esm2020/services/fetch.service.mjs +71 -0
  38. package/esm2020/services/keyboard.service.mjs +128 -0
  39. package/esm2020/types/menu.mjs +2 -0
  40. package/esm2020/types/popup.mjs +2 -0
  41. package/esm2020/utils/index.mjs +39 -0
  42. package/fesm2015/dotglitch-ngx-common.mjs +1997 -0
  43. package/fesm2015/dotglitch-ngx-common.mjs.map +1 -0
  44. package/fesm2020/dotglitch-ngx-common.mjs +1977 -0
  45. package/fesm2020/dotglitch-ngx-common.mjs.map +1 -0
  46. package/index.d.ts +5 -0
  47. package/package.json +60 -0
  48. package/pipes/html-bypass.pipe.d.ts +16 -0
  49. package/pipes/resource-bypass.pipe.d.ts +16 -0
  50. package/pipes/script-bypass.pipe.d.ts +16 -0
  51. package/pipes/style-bypass.pipe.d.ts +16 -0
  52. package/pipes/url-bypass.pipe.d.ts +16 -0
  53. package/public-api.d.ts +34 -0
  54. package/services/dependency.service.d.ts +19 -0
  55. package/services/dialog.service.d.ts +41 -0
  56. package/services/fetch.service.d.ts +28 -0
  57. package/services/keyboard.service.d.ts +79 -0
  58. package/types/menu.d.ts +90 -0
  59. package/types/popup.d.ts +27 -0
  60. package/utils/index.d.ts +19 -0
@@ -0,0 +1,1977 @@
1
+ import * as i0 from '@angular/core';
2
+ import { TemplateRef, Component, Optional, Inject, Input, HostListener, Directive, EventEmitter, Output, Pipe, Injectable, isDevMode, InjectionToken, ViewContainerRef, ViewChild, NgModule } from '@angular/core';
3
+ import * as i1 from '@angular/material/dialog';
4
+ import { MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';
5
+ import * as i2 from '@angular/common';
6
+ import { CommonModule, NgTemplateOutlet, NgIf, NgForOf, DOCUMENT, NgComponentOutlet } from '@angular/common';
7
+ import * as i1$1 from '@angular/platform-browser';
8
+ import { createApplication } from '@angular/platform-browser';
9
+ import { firstValueFrom, of, Subject, debounceTime } from 'rxjs';
10
+ import * as i4 from '@angular/material/icon';
11
+ import { MatIconModule } from '@angular/material/icon';
12
+ import * as i5 from '@angular/material/progress-spinner';
13
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
14
+ import * as i2$1 from '@angular/cdk/portal';
15
+ import { ComponentPortal, PortalModule } from '@angular/cdk/portal';
16
+ import { retry } from 'rxjs/operators';
17
+ import * as i1$2 from '@angular/common/http';
18
+ import * as i2$2 from '@angular/cdk/dialog';
19
+
20
+ /**
21
+ * This utils file exists outside of the strict angular DI zone
22
+ * This enables opening popups without requiring absolute DI bindings.
23
+ */
24
+ const getPosition = (el, config = {}, bounds) => {
25
+ // Bounds of the popup owner
26
+ const src = !!el['nodeName']
27
+ ? el.getBoundingClientRect()
28
+ : {
29
+ // It's a pointer event, so we'll take the X and Y from the pointer.
30
+ x: el['clientX'],
31
+ y: el['clientY'],
32
+ // Set a default tiny size, so we don't divide by zero.
33
+ width: 0.0001,
34
+ height: 0.0001
35
+ };
36
+ // Popup bounds
37
+ const { width, height } = bounds;
38
+ const winh = window.innerHeight;
39
+ const winw = window.innerWidth;
40
+ const cords = {
41
+ top: null,
42
+ left: null
43
+ };
44
+ if (config?.position == "left" || config?.position == "right" || !config?.position) {
45
+ switch (config?.alignment) {
46
+ case "end": {
47
+ // vertically bind to bottom
48
+ cords.top = src.y + src.height - height;
49
+ break;
50
+ }
51
+ case "afterend": {
52
+ // vertically bind below bottom
53
+ cords.top = src.y + src.height;
54
+ break;
55
+ }
56
+ case "beforestart": {
57
+ // vertically bind above top
58
+ cords.top = src.y - height;
59
+ break;
60
+ }
61
+ case "start": {
62
+ // vertically bind to top
63
+ cords.top = src.y;
64
+ break;
65
+ }
66
+ case "center":
67
+ default: {
68
+ // vertically center
69
+ cords.top = (src.y + (src.height / 2)) - (height / 2);
70
+ break;
71
+ }
72
+ }
73
+ // Apply bounds to prevent the dialog from being cut-off screen
74
+ // Lower bound
75
+ cords.top = Math.max(config?.edgePadding || 0, cords.top);
76
+ // Upper bound
77
+ cords.top = Math.min(winh - height, cords.top);
78
+ if (config?.position == "left") {
79
+ cords.left = src.x - (width + (config?.arrowSize || 0) + (config?.arrowPadding || 0));
80
+ }
81
+ if (config?.position == "right" || !config?.position) {
82
+ cords.left = src.x + (src.width + (config?.arrowSize || 0) + (config?.arrowPadding || 0));
83
+ }
84
+ // Lower bound
85
+ cords.left = Math.max(config?.edgePadding || 0, cords.left);
86
+ // Upper bound
87
+ cords.left = Math.min(winw - width, cords.left);
88
+ }
89
+ else if (config?.position == "top" || config?.position == "bottom") {
90
+ switch (config?.alignment) {
91
+ case "end": {
92
+ // vertically bind to right
93
+ cords.left = src.x + src.width - width;
94
+ break;
95
+ }
96
+ case "afterend": {
97
+ // vertically bind past right
98
+ cords.left = src.x + src.width;
99
+ break;
100
+ }
101
+ case "beforestart": {
102
+ // vertically bind before left
103
+ cords.left = src.x - width;
104
+ break;
105
+ }
106
+ case "start": {
107
+ // vertically bind to left
108
+ cords.left = src.x;
109
+ break;
110
+ }
111
+ case "center":
112
+ default: {
113
+ // vertically center
114
+ cords.left = (src.x + (src.width / 2)) - (width / 2);
115
+ break;
116
+ }
117
+ }
118
+ // Apply bounds to prevent the dialog from being cut-off screen
119
+ // Lower bound
120
+ cords.left = Math.max(config?.edgePadding || 0, cords.left);
121
+ // Upper bound
122
+ cords.left = Math.min(winw - width, cords.left);
123
+ if (config?.position == "top") {
124
+ cords.top = src.y - (height + (config?.arrowSize || 0) + (config?.arrowPadding || 0));
125
+ }
126
+ if (config?.position == "bottom") {
127
+ cords.top = src.y + (src.height + (config?.arrowSize || 0) + (config?.arrowPadding || 0));
128
+ }
129
+ // Lower bound
130
+ cords.top = Math.max(config?.edgePadding || 0, cords.top);
131
+ // Upper bound
132
+ cords.top = Math.min(winh - height, cords.top);
133
+ }
134
+ // Assign unit
135
+ cords.top = cords.top + 'px';
136
+ cords.left = cords.left + 'px';
137
+ return cords;
138
+ };
139
+
140
+ const calcTooltipBounds = async (template, data) => {
141
+ const args = {
142
+ data: data || {},
143
+ template,
144
+ config: {},
145
+ selfCords: { left: "0px", top: "0px" },
146
+ ownerCords: { x: 0, y: 0, width: 0, height: 0 },
147
+ id: null
148
+ };
149
+ // Forcibly bootstrap the ctx menu outside of the client application's zone.
150
+ const app = await createApplication({
151
+ providers: [
152
+ { provide: MAT_DIALOG_DATA, useValue: args }
153
+ ]
154
+ });
155
+ const del = document.createElement("div");
156
+ del.style.position = "absolute";
157
+ del.style.left = '-1000vw';
158
+ document.body.append(del);
159
+ const base = app.bootstrap(TooltipComponent, del);
160
+ const { instance } = base;
161
+ await firstValueFrom(app.isStable);
162
+ const el = instance.viewContainer?.element?.nativeElement;
163
+ const rect = el.getBoundingClientRect();
164
+ app.destroy();
165
+ del.remove();
166
+ return rect;
167
+ };
168
+ class TooltipComponent {
169
+ constructor(viewContainer, _data, dialog, // optional only for the purpose of estimating dimensions
170
+ dialogRef) {
171
+ this.viewContainer = viewContainer;
172
+ this._data = _data;
173
+ this.dialog = dialog;
174
+ this.dialogRef = dialogRef;
175
+ this.hasBootstrapped = false;
176
+ this.pointerIsOnVoid = false;
177
+ this.coverRectCords = {
178
+ top: 0,
179
+ left: 0,
180
+ height: 0,
181
+ width: 0
182
+ };
183
+ // Defaults are set before @Input() hooks evaluate
184
+ this.data = this.data || this._data?.data || {};
185
+ this.config = this.config || this._data?.config;
186
+ this.template = this.template || this._data?.template;
187
+ this.ownerCords = this.ownerCords || this._data?.ownerCords;
188
+ this.selfCords = this.selfCords || this._data?.selfCords;
189
+ }
190
+ ngOnInit() {
191
+ const selfY = parseInt(this.selfCords.top.replace('px', ''));
192
+ const selfX = parseInt(this.selfCords.left.replace('px', ''));
193
+ this.coverRectCords = {
194
+ top: this.ownerCords.y - selfY - 16,
195
+ left: this.ownerCords.x - selfX - 16,
196
+ height: this.ownerCords.height + 32,
197
+ width: this.ownerCords.width + 32
198
+ };
199
+ if (this.template instanceof TemplateRef)
200
+ this.isTemplate = true;
201
+ else if (typeof this.template == "function")
202
+ this.isTemplate = false;
203
+ else
204
+ throw new Error("Unrecognized template object provided.");
205
+ // TODO: resolve the event hook with the .void element
206
+ setTimeout(() => {
207
+ this.hasBootstrapped = true;
208
+ if (this.pointerIsOnVoid)
209
+ this.dialogRef.close();
210
+ }, 10);
211
+ }
212
+ /**
213
+ * Close the tooltip if these actions occur
214
+ */
215
+ onClose() {
216
+ this.dialogRef?.close();
217
+ }
218
+ onPointerLeave() {
219
+ this.dialogRef?.close();
220
+ }
221
+ }
222
+ TooltipComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TooltipComponent, deps: [{ token: i0.ViewContainerRef }, { token: MAT_DIALOG_DATA, optional: true }, { token: i1.MatDialog, optional: true }, { token: i1.MatDialogRef, optional: true }], target: i0.ɵɵFactoryTarget.Component });
223
+ TooltipComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: TooltipComponent, isStandalone: true, selector: "ngx-tooltip", inputs: { data: "data", config: "config", ownerCords: "ownerCords", selfCords: "selfCords", template: "template" }, host: { listeners: { "window:resize": "onClose()", "window:blur": "onClose()", "pointerleave": "onPointerLeave()" } }, ngImport: i0, template: "<!-- Mouse event blocker for pointer leave -->\n<div\n *ngIf=\"coverRectCords\"\n class=\"owner-mask\"\n [style.top]=\"coverRectCords.top + 'px'\"\n [style.left]=\"coverRectCords.left + 'px'\"\n [style.height]=\"coverRectCords.height + 'px'\"\n [style.width]=\"coverRectCords.width + 'px'\"\n style=\"z-index: -1;\"\n></div>\n\n<div class=\"void\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && dialogRef.close()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"hasBootstrapped && dialogRef.close()\"\n></div>\n\n<div class=\"container\">\n <ng-container\n *ngIf=\"isTemplate == false\"\n [ngComponentOutlet]=\"$any(template)\"\n >\n </ng-container>\n\n <ng-container\n *ngIf=\"isTemplate == true\"\n >\n <ng-container\n [ngTemplateOutlet]=\"$any(template)\"\n [ngTemplateOutletContext]=\"{ '$implicit': data }\"\n ></ng-container>\n </ng-container>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container .ngx-tooltip{--mdc-dialog-container-color: var(--ngx-tooltip-background-color, #2f2f2f)}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__container{transform-origin:top left}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog--open .mdc-dialog__container{transform:none}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__surface{overflow:visible;background-color:#0000}::ng-deep .cdk-overlay-container .context-menu-backdrop.cdk-overlay-backdrop-showing{opacity:0}::ng-deep .cdk-overlay-pane.ngx-tooltip .mat-dialog-container{padding:0}:host{min-width:2px;min-height:2px;display:block}.void,.owner-mask{position:absolute}.void{top:-100vh;right:-100vw;bottom:-100vh;left:-100vw;z-index:-2}.container{width:100%;height:100%;background:var(--ngx-tooltip-background-color, #333);border-radius:6px;overflow:hidden}\n"], dependencies: [{ kind: "ngmodule", type:
224
+ // NgIf,
225
+ // NgTemplateOutlet,
226
+ // NgComponentOutlet,
227
+ CommonModule }, { kind: "directive", type: i2.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
228
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TooltipComponent, decorators: [{
229
+ type: Component,
230
+ args: [{ selector: 'ngx-tooltip', imports: [
231
+ // NgIf,
232
+ // NgTemplateOutlet,
233
+ // NgComponentOutlet,
234
+ CommonModule,
235
+ ], standalone: true, template: "<!-- Mouse event blocker for pointer leave -->\n<div\n *ngIf=\"coverRectCords\"\n class=\"owner-mask\"\n [style.top]=\"coverRectCords.top + 'px'\"\n [style.left]=\"coverRectCords.left + 'px'\"\n [style.height]=\"coverRectCords.height + 'px'\"\n [style.width]=\"coverRectCords.width + 'px'\"\n style=\"z-index: -1;\"\n></div>\n\n<div class=\"void\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && dialogRef.close()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"hasBootstrapped && dialogRef.close()\"\n></div>\n\n<div class=\"container\">\n <ng-container\n *ngIf=\"isTemplate == false\"\n [ngComponentOutlet]=\"$any(template)\"\n >\n </ng-container>\n\n <ng-container\n *ngIf=\"isTemplate == true\"\n >\n <ng-container\n [ngTemplateOutlet]=\"$any(template)\"\n [ngTemplateOutletContext]=\"{ '$implicit': data }\"\n ></ng-container>\n </ng-container>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container .ngx-tooltip{--mdc-dialog-container-color: var(--ngx-tooltip-background-color, #2f2f2f)}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__container{transform-origin:top left}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog--open .mdc-dialog__container{transform:none}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__surface{overflow:visible;background-color:#0000}::ng-deep .cdk-overlay-container .context-menu-backdrop.cdk-overlay-backdrop-showing{opacity:0}::ng-deep .cdk-overlay-pane.ngx-tooltip .mat-dialog-container{padding:0}:host{min-width:2px;min-height:2px;display:block}.void,.owner-mask{position:absolute}.void{top:-100vh;right:-100vw;bottom:-100vh;left:-100vw;z-index:-2}.container{width:100%;height:100%;background:var(--ngx-tooltip-background-color, #333);border-radius:6px;overflow:hidden}\n"] }]
236
+ }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: undefined, decorators: [{
237
+ type: Optional
238
+ }, {
239
+ type: Inject,
240
+ args: [MAT_DIALOG_DATA]
241
+ }] }, { type: i1.MatDialog, decorators: [{
242
+ type: Optional
243
+ }] }, { type: i1.MatDialogRef, decorators: [{
244
+ type: Optional
245
+ }] }]; }, propDecorators: { data: [{
246
+ type: Input
247
+ }], config: [{
248
+ type: Input
249
+ }], ownerCords: [{
250
+ type: Input
251
+ }], selfCords: [{
252
+ type: Input
253
+ }], template: [{
254
+ type: Input
255
+ }], onClose: [{
256
+ type: HostListener,
257
+ args: ["window:resize"]
258
+ }, {
259
+ type: HostListener,
260
+ args: ["window:blur"]
261
+ }], onPointerLeave: [{
262
+ type: HostListener,
263
+ args: ["pointerleave"]
264
+ }] } });
265
+
266
+ class TooltipDirective {
267
+ constructor(dialog, viewContainer) {
268
+ this.dialog = dialog;
269
+ this.viewContainer = viewContainer;
270
+ /**
271
+ * Configuration for opening the app menu
272
+ */
273
+ this.config = {};
274
+ /**
275
+ * Arbitrary data to pass into the template
276
+ */
277
+ this.data = {};
278
+ }
279
+ ngOnInit() {
280
+ }
281
+ // Needs to be public so we can manually open the dialog
282
+ async onPointerEnter(evt) {
283
+ // If the template is not a template ref, do nothing.
284
+ if (!(this.template instanceof TemplateRef))
285
+ return;
286
+ const el = this.viewContainer.element.nativeElement;
287
+ this.dialogInstance = await openTooltip(this.dialog, this.template, this.data, el, this.config);
288
+ }
289
+ }
290
+ TooltipDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TooltipDirective, deps: [{ token: i1.MatDialog }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
291
+ TooltipDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TooltipDirective, isStandalone: true, selector: "[ngxTooltip],[ngx-tooltip]", inputs: { template: ["ngx-tooltip", "template"], config: ["ngx-tooltip-config", "config"], data: ["ngx-tooltip-context", "data"] }, host: { listeners: { "pointerenter": "onPointerEnter($event)" } }, providers: [
292
+ MatDialog
293
+ ], ngImport: i0 });
294
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TooltipDirective, decorators: [{
295
+ type: Directive,
296
+ args: [{
297
+ selector: '[ngxTooltip],[ngx-tooltip]',
298
+ providers: [
299
+ MatDialog
300
+ ],
301
+ standalone: true
302
+ }]
303
+ }], ctorParameters: function () { return [{ type: i1.MatDialog }, { type: i0.ViewContainerRef }]; }, propDecorators: { template: [{
304
+ type: Input,
305
+ args: ["ngxTooltip"]
306
+ }, {
307
+ type: Input,
308
+ args: ["ngx-tooltip"]
309
+ }], config: [{
310
+ type: Input,
311
+ args: ["ngxTooltipConfig"]
312
+ }, {
313
+ type: Input,
314
+ args: ["ngx-tooltip-config"]
315
+ }], data: [{
316
+ type: Input,
317
+ args: ["ngxTooltipContext"]
318
+ }, {
319
+ type: Input,
320
+ args: ["ngx-tooltip-context"]
321
+ }], onPointerEnter: [{
322
+ type: HostListener,
323
+ args: ['pointerenter', ['$event']]
324
+ }] } });
325
+ // Helper to open the context menu without using the directive.
326
+ const openTooltip = async (dialog, template, data, el, config) => {
327
+ const rect = await calcTooltipBounds(template, data);
328
+ const ownerCords = el.getBoundingClientRect();
329
+ const cords = getPosition(el, config, rect);
330
+ const specificId = crypto.randomUUID();
331
+ return new Promise(res => {
332
+ dialog.open(TooltipComponent, {
333
+ data: {
334
+ data: data,
335
+ template: template,
336
+ config: config,
337
+ ownerCords: ownerCords,
338
+ selfCords: cords,
339
+ id: specificId
340
+ },
341
+ panelClass: ["ngx-tooltip", 'ngx-' + specificId].concat(config?.customClass || []),
342
+ position: cords,
343
+ hasBackdrop: false
344
+ })
345
+ .afterClosed()
346
+ .subscribe(s => {
347
+ res(s);
348
+ });
349
+ });
350
+ };
351
+
352
+ const calcMenuItemBounds = async (menuItems, dataObj) => {
353
+ const data = {
354
+ data: dataObj,
355
+ items: menuItems,
356
+ config: {},
357
+ id: null
358
+ };
359
+ return calcComponentBounds(MenuComponent, data);
360
+ };
361
+ const calcComponentBounds = async (component, data) => {
362
+ // Forcibly bootstrap the ctx menu outside of the client application's zone.
363
+ const app = await createApplication({
364
+ providers: [
365
+ { provide: MAT_DIALOG_DATA, useValue: data }
366
+ ]
367
+ });
368
+ const del = document.createElement("div");
369
+ del.style.position = "absolute";
370
+ del.style.left = '-1000vw';
371
+ document.body.append(del);
372
+ const base = app.bootstrap(component, del);
373
+ const { instance } = base;
374
+ await firstValueFrom(app.isStable);
375
+ const el = instance.viewContainer?.element?.nativeElement;
376
+ const rect = el.getBoundingClientRect();
377
+ app.destroy();
378
+ del.remove();
379
+ return rect;
380
+ };
381
+ class TemplateWrapper {
382
+ constructor(dialogRef, _data, viewContainer) {
383
+ this.dialogRef = dialogRef;
384
+ this._data = _data;
385
+ this.viewContainer = viewContainer;
386
+ this.data = _data.data;
387
+ this.template = _data.template;
388
+ // TODO: This is probably invalid
389
+ this.templateType = this.template instanceof TemplateRef ? "template" : "component";
390
+ if (this.templateType == "component") {
391
+ this.componentPortal = new ComponentPortal(this.template);
392
+ }
393
+ }
394
+ }
395
+ TemplateWrapper.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TemplateWrapper, deps: [{ token: i1.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
396
+ TemplateWrapper.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: TemplateWrapper, isStandalone: true, selector: "ngx-menu-template-wrapper", ngImport: i0, template: `
397
+ <ng-container *ngIf="templateType == 'template'; else portalOutlet">
398
+ <ng-container
399
+ [ngTemplateOutlet]="template"
400
+ [ngTemplateOutletContext]="{ '$implicit': data, dialog: dialogRef }"
401
+ />
402
+ </ng-container>
403
+ <ng-template #portalOutlet [cdkPortalOutlet]="componentPortal" ></ng-template>
404
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2$1.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
405
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TemplateWrapper, decorators: [{
406
+ type: Component,
407
+ args: [{
408
+ selector: 'ngx-menu-template-wrapper',
409
+ template: `
410
+ <ng-container *ngIf="templateType == 'template'; else portalOutlet">
411
+ <ng-container
412
+ [ngTemplateOutlet]="template"
413
+ [ngTemplateOutletContext]="{ '$implicit': data, dialog: dialogRef }"
414
+ />
415
+ </ng-container>
416
+ <ng-template #portalOutlet [cdkPortalOutlet]="componentPortal" ></ng-template>
417
+ `,
418
+ imports: [NgTemplateOutlet, PortalModule, NgIf],
419
+ standalone: true
420
+ }]
421
+ }], ctorParameters: function () { return [{ type: i1.MatDialogRef, decorators: [{
422
+ type: Optional
423
+ }] }, { type: undefined, decorators: [{
424
+ type: Inject,
425
+ args: [MAT_DIALOG_DATA]
426
+ }] }, { type: i0.ViewContainerRef }]; } });
427
+ class MenuComponent {
428
+ constructor(viewContainer, sanitizer, _data, dialog, // optional only for the purpose of estimating dimensions
429
+ dialogRef, changeDetector) {
430
+ this.viewContainer = viewContainer;
431
+ this.sanitizer = sanitizer;
432
+ this._data = _data;
433
+ this.dialog = dialog;
434
+ this.dialogRef = dialogRef;
435
+ this.changeDetector = changeDetector;
436
+ this.closeSignal = new EventEmitter();
437
+ // Check if there are any slashes or dots -- that will clearly exclude it from being a mat icon
438
+ this.matIconRx = /[\/\.]/i;
439
+ this.showIconColumn = true;
440
+ this.showShortcutColumn = true;
441
+ // Defaults are set before @Input() hooks evaluate
442
+ this.data = this._data?.data;
443
+ this.parentCords = this._data?.parentCords;
444
+ this.items = this._data?.items;
445
+ this.config = this._data?.config;
446
+ this.id = this._data?.id;
447
+ }
448
+ ngOnInit() {
449
+ this.items?.forEach(i => {
450
+ if (typeof i == "string")
451
+ return;
452
+ // Set defaults
453
+ i['_disabled'] = false;
454
+ i['_visible'] = true;
455
+ if (i.label)
456
+ try {
457
+ i['_formattedLabel'] = this.formatLabel(i.label);
458
+ }
459
+ catch (e) {
460
+ console.warn(e);
461
+ }
462
+ if (typeof i.isDisabled == "function")
463
+ try {
464
+ i['_disabled'] = i.isDisabled(this.data || {});
465
+ }
466
+ catch (e) {
467
+ console.warn(e);
468
+ }
469
+ if (typeof i.isVisible == "function")
470
+ try {
471
+ i['_visible'] = i.isVisible(this.data || {});
472
+ }
473
+ catch (e) {
474
+ console.warn(e);
475
+ }
476
+ if (typeof i.linkTemplate == "function")
477
+ try {
478
+ i['_link'] = i.linkTemplate(this.data || {});
479
+ }
480
+ catch (e) {
481
+ console.warn(e);
482
+ }
483
+ });
484
+ // Show the icon column if there are any items with an icon
485
+ this.showIconColumn = !!this.items.find(i => typeof i == "object" &&
486
+ typeof i['icon'] == "string" &&
487
+ i['icon'].length > 2);
488
+ this.showShortcutColumn = !!this.items.find(i => typeof i == "object" &&
489
+ typeof i['shortcut'] == "string" &&
490
+ i['shortcut'].length > 2);
491
+ // setTimeout(() => {
492
+ // this.closeOnLeave = true
493
+ // }, 300);
494
+ }
495
+ ngAfterViewInit() {
496
+ if (this.parentCords) {
497
+ this.selfCords = this.viewContainer?.element?.nativeElement?.getBoundingClientRect();
498
+ this.changeDetector.detectChanges();
499
+ }
500
+ }
501
+ /**
502
+ *
503
+ * @param item
504
+ * @param evt
505
+ * @returns
506
+ */
507
+ async onMenuItemClick(item, row, hideBackdrop = false) {
508
+ if (typeof item == 'string')
509
+ return null;
510
+ if (item.separator)
511
+ return null;
512
+ // If cache is enabled, only load if we don't have any children.
513
+ const forceLoad = (item.cacheResolvedChildren ? !item.children : true);
514
+ if (item.childrenResolver && forceLoad) {
515
+ item['_isResolving'] = true;
516
+ item.children = await item.childrenResolver(this.data);
517
+ item['_isResolving'] = false;
518
+ }
519
+ if (!item.childTemplate && !item.children) {
520
+ if (item.action) {
521
+ item.action(this.data);
522
+ this.close();
523
+ }
524
+ // If no action, this is simply a text item.
525
+ return null;
526
+ }
527
+ // Need X pos, Y pos, width and height
528
+ const bounds = row.getBoundingClientRect();
529
+ const cords = {
530
+ top: null,
531
+ left: null,
532
+ bottom: null,
533
+ right: null
534
+ };
535
+ // Set position coordinates
536
+ const { width, height } = await (item.childTemplate
537
+ ? calcComponentBounds(TemplateWrapper, { template: item.childTemplate })
538
+ : calcMenuItemBounds(item.children, this.data));
539
+ if (bounds.y + height > window.innerHeight)
540
+ cords.bottom = "0px";
541
+ if (bounds.x + bounds.width + width > window.innerWidth)
542
+ cords.left = ((bounds.x - width)) + "px";
543
+ if (!cords.bottom)
544
+ cords.top = bounds.y + "px";
545
+ if (!cords.left)
546
+ cords.left = bounds.x + bounds.width + "px";
547
+ const component = item.children ? MenuComponent : TemplateWrapper;
548
+ const dialogRef = this.dialog.open(component, {
549
+ position: cords,
550
+ panelClass: ["ngx-ctx-menu", "ngx-app-menu"].concat(this.config?.customClass || []),
551
+ backdropClass: "ngx-ctx-menu-backdrop",
552
+ hasBackdrop: !hideBackdrop,
553
+ data: {
554
+ data: this.data,
555
+ parentCords: this.viewContainer?.element?.nativeElement?.getBoundingClientRect(),
556
+ items: item.children,
557
+ template: item.childTemplate,
558
+ config: this.config
559
+ }
560
+ });
561
+ let _s = dialogRef
562
+ .afterClosed()
563
+ .subscribe((result) => {
564
+ if (result != -1) {
565
+ if (result && typeof item.action == 'function')
566
+ item.action(result);
567
+ this.close();
568
+ }
569
+ else {
570
+ item['_selfclose'] = Date.now();
571
+ }
572
+ _s.unsubscribe();
573
+ });
574
+ return dialogRef;
575
+ }
576
+ /**
577
+ *
578
+ * @param label
579
+ * @returns
580
+ */
581
+ formatLabel(label) {
582
+ return label.replace(/_([a-z0-9])_/i, (match, group) => `<u>${group}</u>`);
583
+ }
584
+ /**
585
+ * Close the context menu under these circumstances
586
+ */
587
+ // @HostListener("window:resize", ['event'])
588
+ // @HostListener("window:blur", ['event'])
589
+ close() {
590
+ this.closeSignal.emit();
591
+ this.dialogRef?.close();
592
+ }
593
+ /**
594
+ * Check if the dialog is clipping offscreen
595
+ * if so, move it back into view.
596
+ */
597
+ onResize() {
598
+ const el = this.viewContainer?.element?.nativeElement;
599
+ if (!el)
600
+ return;
601
+ const { width, height, x, y } = el.getBoundingClientRect();
602
+ const target = document.querySelector(".ngx-ctx-menu,.ngx-app-menu");
603
+ if (!target)
604
+ return;
605
+ // Move back into view if we're clipping outside of the bottom
606
+ if (y + height > window.innerHeight) {
607
+ const newTop = (window.innerHeight - (height + (this.config.edgePadding || 12))) + "px";
608
+ target.style['margin-top'] = newTop;
609
+ }
610
+ // Move back into view if we're clipping off the right
611
+ if (x + width > window.innerWidth) {
612
+ const newLeft = (window.innerWidth - (width + (this.config.edgePadding || 12))) + "px";
613
+ target.style['margin-left'] = newLeft;
614
+ }
615
+ }
616
+ }
617
+ MenuComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuComponent, deps: [{ token: i0.ViewContainerRef }, { token: i1$1.DomSanitizer }, { token: MAT_DIALOG_DATA, optional: true }, { token: i1.MatDialog, optional: true }, { token: i1.MatDialogRef, optional: true }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
618
+ MenuComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: MenuComponent, isStandalone: true, selector: "ngx-menu", inputs: { data: "data", parentCords: "parentCords", items: "items", config: "config", id: "id" }, outputs: { closeSignal: "closeSignal" }, host: { listeners: { "window:resize": "onResize()" } }, ngImport: i0, template: "<table>\n <tbody>\n <ng-container *ngFor=\"let item of items\">\n <ng-container>\n\n <!-- A row with a click action -->\n <tr #row\n *ngIf=\"item != 'separator' && item.separator != true && item['_visible']\"\n [class.disabled]=\"item['_disabled']\"\n (click)=\"!item['_disabled'] && onMenuItemClick(item, row)\"\n [class.hover]=\"item['children'] && row['hover']\"\n (pointerenter)=\"row['hover'] = true;\"\n (pointerleave)=\"row['hover'] = false\"\n >\n <!-- (item['children']?.length > 0 || item['childTemplate']) && onHover(item, row); closeOnLeave=true -->\n <td class=\"icon\" *ngIf=\"showIconColumn\">\n <img *ngIf=\"matIconRx.test(item.icon); else matIcon\" [src]=\"item.icon\" />\n <ng-template #matIcon>\n <mat-icon [fontIcon]=\"item.icon\"></mat-icon>\n </ng-template>\n </td>\n\n <!-- 'Normal' action based item -->\n <ng-container>\n <td class=\"label\"\n [style.padding-left]=\"showIconColumn ? 0 : '16px'\"\n >\n <a\n [attr.target]=\"item.linkTarget\"\n [attr.href]=\"(item['_link'] || item.link) ? sanitizer.bypassSecurityTrustUrl(item['_link'] || item.link) : undefined\"\n >\n <ng-container\n *ngIf=\"$any(item.labelTemplate)?.prototype; else labelTemplate\"\n [ngTemplateOutlet]=\"$any(item).labelTemplate\"\n [ngTemplateOutletContext]=\"{ '$implicit': data, 'dialog': dialogRef }\"\n />\n\n <ng-template #labelTemplate>\n <ng-container *ngIf=\"!$any(item)?.labelTemplate\">\n <div [innerHTML]=\"item['_formattedLabel']\"></div>\n </ng-container>\n <ng-container *ngIf=\"$any(item)?.labelTemplate\">\n {{$any(item)?.labelTemplate(data || {})}}\n </ng-container>\n </ng-template>\n </a>\n </td>\n </ng-container>\n\n <td class=\"shortcut\" *ngIf=\"showShortcutColumn\">\n {{item.shortcutLabel}}\n </td>\n <td style=\"min-width: 16px\">\n <mat-icon *ngIf=\"\n (item.children && item.children.length > 0) ||\n item.childTemplate ||\n (item.childrenResolver && !item['_isResolving'])\n \"\n sytle=\"transform: translateY(2px)\"\n >\n chevron_right\n </mat-icon>\n\n <mat-progress-spinner *ngIf=\"item['_isResolving']\" mode=\"indeterminate\" [diameter]=\"20\" style=\"margin-right: 4px\"/>\n </td>\n </tr>\n\n <tr *ngIf=\"item != 'separator' && item.separator == true\" class=\"disabled separator\">\n <td class=\"center\" [attr.colspan]=\"2 + (showIconColumn ? 1 : 0) + (showShortcutColumn ? 1 : 0)\">\n <span class=\"hr\">\n {{item['label'] || ''}}\n </span>\n </td>\n </tr>\n <tr *ngIf=\"item == 'separator'\" class=\"disabled separator\">\n <td [attr.colspan]=\"2 + (showIconColumn ? 1 : 0) + (showShortcutColumn ? 1 : 0)\">\n <hr/>\n </td>\n </tr>\n </ng-container>\n </ng-container>\n </tbody>\n</table>\n\n<!-- <div *ngIf=\"true\" class=\"backdrop\"></div> -->\n<!-- <div\n *ngIf=\"parentCords && this.selfCords\"\n class=\"backdrop parent\"\n [style.top]=\"(parentCords.y - selfCords.y + 6) + 'px'\"\n [style.left]=\"(parentCords.x - selfCords.x + 12) + 'px'\"\n [style.width]=\"(parentCords.width) + 'px'\"\n [style.height]=\"(parentCords.height) + 'px'\"\n>\n</div> -->\n<!-- <ng-container *ngIf=\"parentCords && selfCords\">\n <div #top\n class=\"backdrop-outer\"\n [style.bottom]=\"(parentCords.y - selfCords.y)*-1 + parentCords.height + 'px'\"\n style=\"background: #f003;\"\n (pointerenter)=\"onLeave()\"\n ></div>\n <div #right\n class=\"backdrop-outer\"\n [style.left]=\"((parentCords.x - selfCords.x) + parentCords.width) + 'px'\"\n style=\"background: #0f03;\"\n (pointerenter)=\"onLeave()\"\n >\n <div>px: {{parentCords.x}}</div>\n <div>py: {{parentCords.y}}</div>\n <div>pw: {{parentCords.width}}</div>\n <div>ph: {{parentCords.height}}</div>\n <div>sx: {{selfCords.x}}</div>\n <div>sy: {{selfCords.y}}</div>\n <div>sw: {{selfCords.width}}</div>\n <div>sh: {{selfCords.height}}</div>\n </div>\n <div #bottom\n class=\"backdrop-outer\"\n [style.top]=\"((parentCords.y + parentCords.height - selfCords.y)) + 'px'\"\n style=\"background: #00f3;\"\n (pointerenter)=\"onLeave()\"\n ></div>\n <div #left\n class=\"backdrop-outer\"\n [style.right]=\"((parentCords.x - selfCords.x)*-1 + parentCords.width + 32) + 'px'\"\n style=\"background: #fff3;\"\n (pointerenter)=\"onLeave()\"\n ></div>\n</ng-container> -->\n", styles: ["::ng-deep .cdk-overlay-container .ngx-ctx-menu{--mdc-dialog-container-color: var(--ngx-ctx-menu-background-color, #2f2f2f)}::ng-deep .cdk-overlay-container .ngx-ctx-menu .mdc-dialog__container{transform-origin:top left}::ng-deep .cdk-overlay-container .ngx-ctx-menu .mdc-dialog--open .mdc-dialog__container{transform:none}::ng-deep .cdk-overlay-pane.ngx-ctx-menu .mat-mdc-dialog-surface{overflow:visible}:host{-webkit-user-select:none;user-select:none;z-index:1;position:relative;display:block;overflow:hidden auto}table{border-spacing:0;border-radius:5px;padding:4px 0}tr{color:var(--ngx-ctx-menu-text-color, #ccc);font-size:14px;cursor:pointer;transition:background-color 75ms ease,color 75ms ease}tr:not(.disabled):hover{background-color:var(--ngx-ctx-menu-hover-background-color, #94ebeb);color:var(--ngx-ctx-menu-hover-text-color, #000)}tr:not(.disabled):hover a{color:var(--ngx-ctx-menu-hover-text-color, #000)}tr:not(.separator){height:36px}tr.disabled .label{color:var(--ngx-ctx-menu-disabled-text-color, #919191)}tr .center{text-align:center}tr a{outline:0;display:flex;align-items:center;gap:10px;justify-content:space-between;height:100%;width:100%}tr .label{min-width:100px}.hr{height:1px;text-align:center;position:relative}.hr:before,.hr:after{content:\"\";background:var(--ngx-ctx-menu-separator-color, #2a2a2a);display:block;position:absolute;top:0;bottom:0;height:1px;margin:auto;width:300px}.hr:before{right:calc(100% + 4px)}.hr:after{left:calc(100% + 4px)}hr{background:var(--ngx-ctx-menu-separator-color, #2a2a2a);border:0;height:1px;margin:0}.icon{width:24px;height:24px;padding-left:10px}.icon mat-icon{transform:translateY(2px)}.shortcut{color:var(--ngx-ctx-menu-shortcut-text-color, #848484);text-align:end;padding-right:10px;padding-left:12px}.label{height:var(--ngx-ctx-menu-item-height, 30px)}td{vertical-align:middle}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i5.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] });
619
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuComponent, decorators: [{
620
+ type: Component,
621
+ args: [{ selector: 'ngx-menu', imports: [
622
+ NgIf,
623
+ NgForOf,
624
+ NgTemplateOutlet,
625
+ MatIconModule,
626
+ MatProgressSpinnerModule
627
+ ], standalone: true, template: "<table>\n <tbody>\n <ng-container *ngFor=\"let item of items\">\n <ng-container>\n\n <!-- A row with a click action -->\n <tr #row\n *ngIf=\"item != 'separator' && item.separator != true && item['_visible']\"\n [class.disabled]=\"item['_disabled']\"\n (click)=\"!item['_disabled'] && onMenuItemClick(item, row)\"\n [class.hover]=\"item['children'] && row['hover']\"\n (pointerenter)=\"row['hover'] = true;\"\n (pointerleave)=\"row['hover'] = false\"\n >\n <!-- (item['children']?.length > 0 || item['childTemplate']) && onHover(item, row); closeOnLeave=true -->\n <td class=\"icon\" *ngIf=\"showIconColumn\">\n <img *ngIf=\"matIconRx.test(item.icon); else matIcon\" [src]=\"item.icon\" />\n <ng-template #matIcon>\n <mat-icon [fontIcon]=\"item.icon\"></mat-icon>\n </ng-template>\n </td>\n\n <!-- 'Normal' action based item -->\n <ng-container>\n <td class=\"label\"\n [style.padding-left]=\"showIconColumn ? 0 : '16px'\"\n >\n <a\n [attr.target]=\"item.linkTarget\"\n [attr.href]=\"(item['_link'] || item.link) ? sanitizer.bypassSecurityTrustUrl(item['_link'] || item.link) : undefined\"\n >\n <ng-container\n *ngIf=\"$any(item.labelTemplate)?.prototype; else labelTemplate\"\n [ngTemplateOutlet]=\"$any(item).labelTemplate\"\n [ngTemplateOutletContext]=\"{ '$implicit': data, 'dialog': dialogRef }\"\n />\n\n <ng-template #labelTemplate>\n <ng-container *ngIf=\"!$any(item)?.labelTemplate\">\n <div [innerHTML]=\"item['_formattedLabel']\"></div>\n </ng-container>\n <ng-container *ngIf=\"$any(item)?.labelTemplate\">\n {{$any(item)?.labelTemplate(data || {})}}\n </ng-container>\n </ng-template>\n </a>\n </td>\n </ng-container>\n\n <td class=\"shortcut\" *ngIf=\"showShortcutColumn\">\n {{item.shortcutLabel}}\n </td>\n <td style=\"min-width: 16px\">\n <mat-icon *ngIf=\"\n (item.children && item.children.length > 0) ||\n item.childTemplate ||\n (item.childrenResolver && !item['_isResolving'])\n \"\n sytle=\"transform: translateY(2px)\"\n >\n chevron_right\n </mat-icon>\n\n <mat-progress-spinner *ngIf=\"item['_isResolving']\" mode=\"indeterminate\" [diameter]=\"20\" style=\"margin-right: 4px\"/>\n </td>\n </tr>\n\n <tr *ngIf=\"item != 'separator' && item.separator == true\" class=\"disabled separator\">\n <td class=\"center\" [attr.colspan]=\"2 + (showIconColumn ? 1 : 0) + (showShortcutColumn ? 1 : 0)\">\n <span class=\"hr\">\n {{item['label'] || ''}}\n </span>\n </td>\n </tr>\n <tr *ngIf=\"item == 'separator'\" class=\"disabled separator\">\n <td [attr.colspan]=\"2 + (showIconColumn ? 1 : 0) + (showShortcutColumn ? 1 : 0)\">\n <hr/>\n </td>\n </tr>\n </ng-container>\n </ng-container>\n </tbody>\n</table>\n\n<!-- <div *ngIf=\"true\" class=\"backdrop\"></div> -->\n<!-- <div\n *ngIf=\"parentCords && this.selfCords\"\n class=\"backdrop parent\"\n [style.top]=\"(parentCords.y - selfCords.y + 6) + 'px'\"\n [style.left]=\"(parentCords.x - selfCords.x + 12) + 'px'\"\n [style.width]=\"(parentCords.width) + 'px'\"\n [style.height]=\"(parentCords.height) + 'px'\"\n>\n</div> -->\n<!-- <ng-container *ngIf=\"parentCords && selfCords\">\n <div #top\n class=\"backdrop-outer\"\n [style.bottom]=\"(parentCords.y - selfCords.y)*-1 + parentCords.height + 'px'\"\n style=\"background: #f003;\"\n (pointerenter)=\"onLeave()\"\n ></div>\n <div #right\n class=\"backdrop-outer\"\n [style.left]=\"((parentCords.x - selfCords.x) + parentCords.width) + 'px'\"\n style=\"background: #0f03;\"\n (pointerenter)=\"onLeave()\"\n >\n <div>px: {{parentCords.x}}</div>\n <div>py: {{parentCords.y}}</div>\n <div>pw: {{parentCords.width}}</div>\n <div>ph: {{parentCords.height}}</div>\n <div>sx: {{selfCords.x}}</div>\n <div>sy: {{selfCords.y}}</div>\n <div>sw: {{selfCords.width}}</div>\n <div>sh: {{selfCords.height}}</div>\n </div>\n <div #bottom\n class=\"backdrop-outer\"\n [style.top]=\"((parentCords.y + parentCords.height - selfCords.y)) + 'px'\"\n style=\"background: #00f3;\"\n (pointerenter)=\"onLeave()\"\n ></div>\n <div #left\n class=\"backdrop-outer\"\n [style.right]=\"((parentCords.x - selfCords.x)*-1 + parentCords.width + 32) + 'px'\"\n style=\"background: #fff3;\"\n (pointerenter)=\"onLeave()\"\n ></div>\n</ng-container> -->\n", styles: ["::ng-deep .cdk-overlay-container .ngx-ctx-menu{--mdc-dialog-container-color: var(--ngx-ctx-menu-background-color, #2f2f2f)}::ng-deep .cdk-overlay-container .ngx-ctx-menu .mdc-dialog__container{transform-origin:top left}::ng-deep .cdk-overlay-container .ngx-ctx-menu .mdc-dialog--open .mdc-dialog__container{transform:none}::ng-deep .cdk-overlay-pane.ngx-ctx-menu .mat-mdc-dialog-surface{overflow:visible}:host{-webkit-user-select:none;user-select:none;z-index:1;position:relative;display:block;overflow:hidden auto}table{border-spacing:0;border-radius:5px;padding:4px 0}tr{color:var(--ngx-ctx-menu-text-color, #ccc);font-size:14px;cursor:pointer;transition:background-color 75ms ease,color 75ms ease}tr:not(.disabled):hover{background-color:var(--ngx-ctx-menu-hover-background-color, #94ebeb);color:var(--ngx-ctx-menu-hover-text-color, #000)}tr:not(.disabled):hover a{color:var(--ngx-ctx-menu-hover-text-color, #000)}tr:not(.separator){height:36px}tr.disabled .label{color:var(--ngx-ctx-menu-disabled-text-color, #919191)}tr .center{text-align:center}tr a{outline:0;display:flex;align-items:center;gap:10px;justify-content:space-between;height:100%;width:100%}tr .label{min-width:100px}.hr{height:1px;text-align:center;position:relative}.hr:before,.hr:after{content:\"\";background:var(--ngx-ctx-menu-separator-color, #2a2a2a);display:block;position:absolute;top:0;bottom:0;height:1px;margin:auto;width:300px}.hr:before{right:calc(100% + 4px)}.hr:after{left:calc(100% + 4px)}hr{background:var(--ngx-ctx-menu-separator-color, #2a2a2a);border:0;height:1px;margin:0}.icon{width:24px;height:24px;padding-left:10px}.icon mat-icon{transform:translateY(2px)}.shortcut{color:var(--ngx-ctx-menu-shortcut-text-color, #848484);text-align:end;padding-right:10px;padding-left:12px}.label{height:var(--ngx-ctx-menu-item-height, 30px)}td{vertical-align:middle}\n"] }]
628
+ }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i1$1.DomSanitizer }, { type: undefined, decorators: [{
629
+ type: Optional
630
+ }, {
631
+ type: Inject,
632
+ args: [MAT_DIALOG_DATA]
633
+ }] }, { type: i1.MatDialog, decorators: [{
634
+ type: Optional
635
+ }] }, { type: i1.MatDialogRef, decorators: [{
636
+ type: Optional
637
+ }] }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { data: [{
638
+ type: Input
639
+ }], parentCords: [{
640
+ type: Input
641
+ }], items: [{
642
+ type: Input
643
+ }], config: [{
644
+ type: Input
645
+ }], id: [{
646
+ type: Input
647
+ }], closeSignal: [{
648
+ type: Output
649
+ }], onResize: [{
650
+ type: HostListener,
651
+ args: ["window:resize"]
652
+ }] } });
653
+
654
+ class MenuDirective {
655
+ constructor(dialog, viewContainer) {
656
+ this.dialog = dialog;
657
+ this.viewContainer = viewContainer;
658
+ /**
659
+ * Configuration for opening the app menu
660
+ */
661
+ this.config = {};
662
+ }
663
+ ngAfterViewInit() {
664
+ const el = this.viewContainer.element.nativeElement;
665
+ // Automatically attach context menu items to
666
+ // the contextmenu event
667
+ if (this.ctxMenuItems) {
668
+ el.onclick = this.openMenu.bind(this);
669
+ el.addEventListener('contextmenu', (e) => {
670
+ e.preventDefault();
671
+ this.openMenu(e);
672
+ });
673
+ }
674
+ if (!this.config?.trigger) {
675
+ el.onclick = this.openMenu.bind(this);
676
+ el.addEventListener('click', this.openMenu.bind(this));
677
+ }
678
+ else {
679
+ const triggers = Array.isArray(this.config.trigger) ? this.config.trigger : [this.config.trigger];
680
+ triggers.forEach(t => {
681
+ if (t == "contextmenu") {
682
+ el.addEventListener(t, (e) => {
683
+ e.preventDefault();
684
+ this.openMenu(e);
685
+ });
686
+ }
687
+ else {
688
+ el.addEventListener(t, this.openMenu.bind(this));
689
+ }
690
+ });
691
+ }
692
+ }
693
+ async openMenu(evt) {
694
+ const el = this.viewContainer.element.nativeElement;
695
+ el.classList.add("ngx-menu-open");
696
+ return openMenu(this.dialog, this.menuItems, this.data, evt, this.config)
697
+ .then((...res) => {
698
+ el.classList.remove("ngx-menu-open");
699
+ return res;
700
+ })
701
+ .catch((ex) => {
702
+ el.classList.remove("ngx-menu-open");
703
+ throw ex;
704
+ });
705
+ }
706
+ }
707
+ MenuDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuDirective, deps: [{ token: i1.MatDialog }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
708
+ MenuDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: MenuDirective, isStandalone: true, selector: "[ngx-ctxmenu],[ngx-menu]", inputs: { data: ["ngx-menu-context", "data"], ctxMenuItems: ["ngx-ctxmenu", "ctxMenuItems"], menuItems: ["ngx-menu", "menuItems"], config: ["ngx-menu-config", "config"] }, providers: [
709
+ MatDialog
710
+ ], ngImport: i0 });
711
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: MenuDirective, decorators: [{
712
+ type: Directive,
713
+ args: [{
714
+ selector: '[ngx-ctxmenu],[ngx-menu]',
715
+ providers: [
716
+ MatDialog
717
+ ],
718
+ standalone: true
719
+ }]
720
+ }], ctorParameters: function () { return [{ type: i1.MatDialog }, { type: i0.ViewContainerRef }]; }, propDecorators: { data: [{
721
+ type: Input,
722
+ args: ["ngx-menu-context"]
723
+ }], ctxMenuItems: [{
724
+ type: Input,
725
+ args: ["ngx-ctxmenu"]
726
+ }], menuItems: [{
727
+ type: Input,
728
+ args: ["ngx-menu"]
729
+ }], config: [{
730
+ type: Input,
731
+ args: ["ngx-menu-config"]
732
+ }] } });
733
+ // Helper to open the context menu without using the directive.
734
+ const openMenu = async (dialog, menuItems, data, evt, config = {}) => {
735
+ evt.preventDefault();
736
+ evt.stopPropagation();
737
+ const cords = getPosition(evt, config, await calcMenuItemBounds(menuItems, data));
738
+ const specificId = crypto.randomUUID();
739
+ if (!config.alignment)
740
+ config.alignment = "start";
741
+ return new Promise(res => {
742
+ dialog.open(MenuComponent, {
743
+ data: {
744
+ data: data,
745
+ items: menuItems,
746
+ config: config,
747
+ id: specificId
748
+ },
749
+ panelClass: ["ngx-menu", 'ngx-' + specificId].concat(config?.customClass || []),
750
+ position: cords,
751
+ backdropClass: "ngx-menu-backdrop"
752
+ })
753
+ .afterClosed()
754
+ .subscribe(s => {
755
+ res(s);
756
+ });
757
+ });
758
+ };
759
+
760
+ /**
761
+ * Url Sanitizer pipe.
762
+ *
763
+ * This trusts URLs that exist in a safe list defined in our environments.ts file.
764
+ * Any other URLs will NOT be trusted, thus will not be loaded.
765
+ */
766
+ class HtmlBypass {
767
+ constructor(sanitizer) {
768
+ this.sanitizer = sanitizer;
769
+ }
770
+ transform(url) {
771
+ return this.sanitizer.bypassSecurityTrustHtml(url);
772
+ }
773
+ }
774
+ HtmlBypass.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HtmlBypass, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
775
+ HtmlBypass.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: HtmlBypass, isStandalone: true, name: "htmlbypass" });
776
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: HtmlBypass, decorators: [{
777
+ type: Pipe,
778
+ args: [{
779
+ name: 'htmlbypass',
780
+ standalone: true
781
+ }]
782
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
783
+
784
+ /**
785
+ * Url Sanitizer pipe.
786
+ *
787
+ * This trusts URLs that exist in a safe list defined in our environments.ts file.
788
+ * Any other URLs will NOT be trusted, thus will not be loaded.
789
+ */
790
+ class ResourceBypass {
791
+ constructor(sanitizer) {
792
+ this.sanitizer = sanitizer;
793
+ }
794
+ transform(url) {
795
+ return this.sanitizer.bypassSecurityTrustResourceUrl(url);
796
+ }
797
+ }
798
+ ResourceBypass.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ResourceBypass, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
799
+ ResourceBypass.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ResourceBypass, isStandalone: true, name: "resourcebypass" });
800
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ResourceBypass, decorators: [{
801
+ type: Pipe,
802
+ args: [{
803
+ name: 'resourcebypass',
804
+ standalone: true
805
+ }]
806
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
807
+
808
+ /**
809
+ * Url Sanitizer pipe.
810
+ *
811
+ * This trusts URLs that exist in a safe list defined in our environments.ts file.
812
+ * Any other URLs will NOT be trusted, thus will not be loaded.
813
+ */
814
+ class ScriptBypass {
815
+ constructor(sanitizer) {
816
+ this.sanitizer = sanitizer;
817
+ }
818
+ transform(url) {
819
+ return this.sanitizer.bypassSecurityTrustScript(url);
820
+ }
821
+ }
822
+ ScriptBypass.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ScriptBypass, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
823
+ ScriptBypass.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ScriptBypass, isStandalone: true, name: "scriptbypass" });
824
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ScriptBypass, decorators: [{
825
+ type: Pipe,
826
+ args: [{
827
+ name: 'scriptbypass',
828
+ standalone: true
829
+ }]
830
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
831
+
832
+ /**
833
+ * Url Sanitizer pipe.
834
+ *
835
+ * This trusts URLs that exist in a safe list defined in our environments.ts file.
836
+ * Any other URLs will NOT be trusted, thus will not be loaded.
837
+ */
838
+ class StyleBypass {
839
+ constructor(sanitizer) {
840
+ this.sanitizer = sanitizer;
841
+ }
842
+ transform(url) {
843
+ return this.sanitizer.bypassSecurityTrustStyle(url);
844
+ }
845
+ }
846
+ StyleBypass.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: StyleBypass, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
847
+ StyleBypass.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: StyleBypass, isStandalone: true, name: "stylebypass" });
848
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: StyleBypass, decorators: [{
849
+ type: Pipe,
850
+ args: [{
851
+ name: 'stylebypass',
852
+ standalone: true
853
+ }]
854
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
855
+
856
+ /**
857
+ * Url Sanitizer pipe.
858
+ *
859
+ * This trusts URLs that exist in a safe list defined in our environments.ts file.
860
+ * Any other URLs will NOT be trusted, thus will not be loaded.
861
+ */
862
+ class UrlBypass {
863
+ constructor(sanitizer) {
864
+ this.sanitizer = sanitizer;
865
+ }
866
+ transform(url) {
867
+ return this.sanitizer.bypassSecurityTrustUrl(url);
868
+ }
869
+ }
870
+ UrlBypass.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UrlBypass, deps: [{ token: i1$1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
871
+ UrlBypass.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: UrlBypass, isStandalone: true, name: "urlbypass" });
872
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: UrlBypass, decorators: [{
873
+ type: Pipe,
874
+ args: [{
875
+ name: 'urlbypass',
876
+ standalone: true
877
+ }]
878
+ }], ctorParameters: function () { return [{ type: i1$1.DomSanitizer }]; } });
879
+
880
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
881
+ /**
882
+ * Prompt the user to save a json file of the given object.
883
+ */
884
+ const saveObjectAsFile = (name, data) => {
885
+ const a = document.createElement("a");
886
+ const file = new Blob([JSON.stringify(data)], { type: "application/json" });
887
+ a.href = URL.createObjectURL(file);
888
+ a.download = name;
889
+ a.click();
890
+ a.remove();
891
+ };
892
+ /**
893
+ * Formatted logger that will print a bit of context before the message.
894
+ * @returns
895
+ */
896
+ const Logger = (context, contextColor, textColor = "#03a9f4") => ({
897
+ log: (message, ...args) => {
898
+ console.log(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args);
899
+ },
900
+ warn: (message, ...args) => {
901
+ console.warn(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args);
902
+ },
903
+ err: (message, ...args) => {
904
+ console.error(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args);
905
+ },
906
+ error: (message, ...args) => {
907
+ console.error(`%c[${context}] %c${message}`, 'color: ' + contextColor, 'color: ' + textColor, ...args);
908
+ }
909
+ });
910
+ /**
911
+ * Convert a string `fooBAR baz_160054''"1]"` into a slug: `foobar-baz-1600541`
912
+ */
913
+ const stringToSlug = (text) => (text || '')
914
+ .trim()
915
+ .toLowerCase()
916
+ .replace(/[\-_+ ]/g, '-')
917
+ .replace(/[^a-z0-9\-]/g, '');
918
+
919
+ const SCRIPT_INIT_TIMEOUT = 500; // ms
920
+ /**
921
+ * Service that installs CSS/JS dynamically
922
+ */
923
+ class DependencyService {
924
+ constructor(document) {
925
+ this.document = document;
926
+ }
927
+ /**
928
+ * Install a Javascript file into the webpage on-demand
929
+ * @param id Unique identifier for the JS script
930
+ * @param src URL of the script
931
+ * @param globalkey A global object the script will provide.
932
+ * Providing this will ensure a promise only resolves after the
933
+ * specified global object is provided, with a timeout of 500ms
934
+ */
935
+ loadScript(id, src, globalkey = null) {
936
+ return new Promise((res, rej) => {
937
+ if (this.document.getElementById(id))
938
+ return res();
939
+ const script = this.document.createElement('script');
940
+ script.id = id;
941
+ script.setAttribute("async", '');
942
+ script.setAttribute("src", src);
943
+ script.onload = async () => {
944
+ if (typeof globalkey == "string") {
945
+ let i = 0;
946
+ for (; !window[globalkey] && i < SCRIPT_INIT_TIMEOUT; i += 10)
947
+ await sleep(10);
948
+ if (i >= SCRIPT_INIT_TIMEOUT) {
949
+ return rej(new Error("Timed out waiting for script to self-initialize."));
950
+ }
951
+ }
952
+ res();
953
+ };
954
+ this.document.body.appendChild(script);
955
+ });
956
+ }
957
+ }
958
+ DependencyService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DependencyService, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
959
+ DependencyService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DependencyService, providedIn: 'root' });
960
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DependencyService, decorators: [{
961
+ type: Injectable,
962
+ args: [{
963
+ providedIn: 'root'
964
+ }]
965
+ }], ctorParameters: function () { return [{ type: Document, decorators: [{
966
+ type: Inject,
967
+ args: [DOCUMENT]
968
+ }] }]; } });
969
+
970
+ const { log, warn, err } = Logger("DialogService", "#607d8b");
971
+ class DialogService {
972
+ constructor(dialog, lazyLoader) {
973
+ this.dialog = dialog;
974
+ this.lazyLoader = lazyLoader;
975
+ this.dialogs = [];
976
+ }
977
+ open(name, opts = {}) {
978
+ return new Promise((resolve, reject) => {
979
+ const registration = this.lazyLoader.resolveRegistrationEntry(name, opts.group || "default");
980
+ if (!registration)
981
+ return reject(new Error("Cannot open dialog for " + name + ". Could not find in registry."));
982
+ const args = {
983
+ closeOnNavigation: true,
984
+ restoreFocus: true,
985
+ width: registration['width'],
986
+ height: registration['height'],
987
+ ...opts,
988
+ data: {
989
+ id: name,
990
+ inputs: opts.inputs || {},
991
+ outputs: opts.outputs || {},
992
+ group: opts.group
993
+ },
994
+ panelClass: [
995
+ "dialog-" + name,
996
+ ...(Array.isArray(opts.panelClass) ? opts.panelClass : [opts.panelClass] || [])
997
+ ]
998
+ };
999
+ // TODO:
1000
+ let dialog = this.dialog.open(undefined, args);
1001
+ dialog['idx'] = name;
1002
+ this.dialogs.push(dialog);
1003
+ dialog.afterClosed().subscribe(result => {
1004
+ log("Dialog closed " + name, result);
1005
+ resolve(result);
1006
+ });
1007
+ });
1008
+ }
1009
+ // Close all dialogs matching the given name
1010
+ close(name) {
1011
+ const dialogs = this.dialogs.filter(d => d['idx'] == name);
1012
+ dialogs.forEach(dialog => dialog.close());
1013
+ }
1014
+ /**
1015
+ * Method to close _all_ dialogs.
1016
+ * Should be used sparingly.
1017
+ */
1018
+ clearDialog() {
1019
+ this.dialogs.forEach(dialog => dialog.close());
1020
+ }
1021
+ }
1022
+ DialogService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DialogService, deps: [{ token: i1.MatDialog }, { token: LazyLoaderService }], target: i0.ɵɵFactoryTarget.Injectable });
1023
+ DialogService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DialogService, providedIn: 'root' });
1024
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DialogService, decorators: [{
1025
+ type: Injectable,
1026
+ args: [{
1027
+ providedIn: 'root'
1028
+ }]
1029
+ }], ctorParameters: function () { return [{ type: i1.MatDialog }, { type: LazyLoaderService }]; } });
1030
+
1031
+ // Total number of _retries_ if there is a 429 response code.
1032
+ const retryCount = 2;
1033
+ class Fetch {
1034
+ constructor(http) {
1035
+ this.http = http;
1036
+ }
1037
+ // Public interface for making AJAX transactions
1038
+ get(url, options = {}, returnError = false) {
1039
+ return this.request("get", url, options, returnError);
1040
+ }
1041
+ put(url, body, options = {}, returnError = false) {
1042
+ options.body = (options.body && Object.keys(options.body).length > 0 ? options.body : body) || {};
1043
+ return this.request("put", url, options, returnError);
1044
+ }
1045
+ post(url, body, options = {}, returnError = false) {
1046
+ options.body = (options.body && Object.keys(options.body).length > 0 ? options.body : body) || {};
1047
+ return this.request("post", url, options, returnError);
1048
+ }
1049
+ patch(url, body, options = {}, returnError = false) {
1050
+ options.body = (options.body && Object.keys(options.body).length > 0 ? options.body : body) || {};
1051
+ return this.request("patch", url, options, returnError);
1052
+ }
1053
+ delete(url, options = {}, returnError = false) {
1054
+ return this.request("delete", url, options, returnError);
1055
+ }
1056
+ // Internally, handle the observable as a promise.
1057
+ request(method, url, options = {}, returnError = false) {
1058
+ options.reportProgress = true;
1059
+ // Allow support for different response types.
1060
+ // Generally we shouldn't need this to be anything other than JSON.
1061
+ options.responseType = options.responseType || "json";
1062
+ options.withCredentials = true;
1063
+ const p = new Promise((resolve, reject) => {
1064
+ const o = this.http.request(method, url, options)
1065
+ .pipe(retry({
1066
+ delay(error, retryCount) {
1067
+ // 429 and 502 are most common for overloaded
1068
+ // backends -- so we'll retry if we get these errors
1069
+ if (error.status == 429 || error.status == 502)
1070
+ return of({});
1071
+ if (error.status == 504 && isDevMode())
1072
+ alert("It looks like you can't reach your development backend anymore");
1073
+ throw error;
1074
+ },
1075
+ count: retryCount
1076
+ }))
1077
+ .subscribe(data => {
1078
+ resolve(data);
1079
+ // provide 3ms slacktime before releasing observable.
1080
+ setTimeout(() => {
1081
+ o.unsubscribe();
1082
+ }, 3);
1083
+ });
1084
+ });
1085
+ return p;
1086
+ }
1087
+ }
1088
+ Fetch.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: Fetch, deps: [{ token: i1$2.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
1089
+ Fetch.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: Fetch, providedIn: "root" });
1090
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: Fetch, decorators: [{
1091
+ type: Injectable,
1092
+ args: [{
1093
+ providedIn: "root"
1094
+ }]
1095
+ }], ctorParameters: function () { return [{ type: i1$2.HttpClient }]; } });
1096
+
1097
+ /**
1098
+ * Service that listens for global keyboard events
1099
+ */
1100
+ class KeyboardService {
1101
+ constructor() {
1102
+ this.heldKeys = {};
1103
+ this.keyCommands = [];
1104
+ window.addEventListener("keydown", (evt) => this.onKeyDown(evt));
1105
+ window.addEventListener("keyup", (evt) => this.onKeyUp(evt));
1106
+ }
1107
+ onKeyDown(evt) {
1108
+ // console.log("keydown", evt.key)
1109
+ this.heldKeys[evt.key.toLowerCase()] = true;
1110
+ // Do a general filter where all of the modifiers must be matched if specified
1111
+ // Then check that the actual keys match what was specified
1112
+ let commands = this.keyCommands
1113
+ .filter(kc => (kc.ctrl == undefined || kc.ctrl === evt.ctrlKey) &&
1114
+ (kc.alt == undefined || kc.alt === evt.altKey) &&
1115
+ (kc.shift == undefined || kc.shift === evt.shiftKey) &&
1116
+ (kc.super == undefined || kc.super === evt.metaKey) &&
1117
+ kc.keys.length == kc.keys.filter(k => this.heldKeys[k])?.length);
1118
+ if (evt.ctrlKey && commands.length > 0 || commands.find(c => c.interrupt)) {
1119
+ evt.stopPropagation();
1120
+ evt.preventDefault();
1121
+ }
1122
+ if (evt.key == "Pause")
1123
+ debugger;
1124
+ commands.forEach(kc => kc.sub.next(evt));
1125
+ /**
1126
+ * Prevent CTRL+P and other standard key events from being handled by the browser.
1127
+ * Allow specific combonations:
1128
+ * CTRL+W
1129
+ * CTRL+T
1130
+ * CTRL+F5
1131
+ */
1132
+ // if (evt.ctrlKey && !['w', 't', 'F5'].includes(evt.key)) {
1133
+ // evt.preventDefault();
1134
+ // }
1135
+ }
1136
+ onKeyUp(evt) {
1137
+ this.heldKeys[evt.key.toLowerCase()] = false;
1138
+ }
1139
+ onKeyPress(evt) {
1140
+ // this.heldKeys[evt.key] = false;
1141
+ }
1142
+ /**
1143
+ * Use this to subscribe to keyboard events throughout
1144
+ * the application. This is a passive listener and will
1145
+ * **NOT** interrupt the event chain.
1146
+ */
1147
+ onKeyCommand(key) {
1148
+ const sub = new Subject();
1149
+ let item = {
1150
+ ...key,
1151
+ keys: (Array.isArray(key.key) ? key.key : [key.key]),
1152
+ sub: sub
1153
+ };
1154
+ this.keyCommands.push(item);
1155
+ return {
1156
+ ...sub,
1157
+ subscribe: ((...args) => {
1158
+ const s = sub.subscribe(...args);
1159
+ return {
1160
+ ...s,
1161
+ unsubscribe: () => {
1162
+ s.unsubscribe();
1163
+ // Remove the keycommand from the list of listeners.
1164
+ const i = this.keyCommands.findIndex(c => c == item);
1165
+ this.keyCommands.splice(i, 1);
1166
+ }
1167
+ };
1168
+ })
1169
+ };
1170
+ }
1171
+ /**
1172
+ * Return `true` if shift is currently pressed.
1173
+ */
1174
+ get isShiftPressed() {
1175
+ return !!this.heldKeys["shift"];
1176
+ }
1177
+ /**
1178
+ * Return `true` if ctrl is currently pressed.
1179
+ */
1180
+ get isCtrlPressed() {
1181
+ return !!this.heldKeys["control"];
1182
+ }
1183
+ /**
1184
+ * Return `true` if alt is currently pressed.
1185
+ */
1186
+ get isAltPressed() {
1187
+ return !!this.heldKeys["alt"];
1188
+ }
1189
+ /**
1190
+ * Return `true` if super (mac/linux) or the windows key is currently pressed.
1191
+ */
1192
+ get isSuperPressed() {
1193
+ return !!this.heldKeys["super"];
1194
+ }
1195
+ /**
1196
+ * Return `true` if tab is currently pressed.
1197
+ */
1198
+ get isTabPressed() {
1199
+ return !!this.heldKeys["tab"];
1200
+ }
1201
+ clearKeys() {
1202
+ Object.keys(this.heldKeys).forEach(k => {
1203
+ this.heldKeys[k] = false;
1204
+ });
1205
+ }
1206
+ }
1207
+ KeyboardService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: KeyboardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1208
+ KeyboardService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: KeyboardService, providedIn: 'root' });
1209
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: KeyboardService, decorators: [{
1210
+ type: Injectable,
1211
+ args: [{
1212
+ providedIn: 'root'
1213
+ }]
1214
+ }], ctorParameters: function () { return []; }, propDecorators: { clearKeys: [{
1215
+ type: HostListener,
1216
+ args: ["window:blur"]
1217
+ }, {
1218
+ type: HostListener,
1219
+ args: ["window:resize"]
1220
+ }] } });
1221
+
1222
+ var ComponentResolveStrategy;
1223
+ (function (ComponentResolveStrategy) {
1224
+ /**
1225
+ * Match the fist component we find
1226
+ * (best used for standalone components)
1227
+ * @default
1228
+ */
1229
+ ComponentResolveStrategy[ComponentResolveStrategy["PickFirst"] = 0] = "PickFirst";
1230
+ /**
1231
+ * Perform an Exact ID to Classname of the Component
1232
+ * case sensitive, zero tolerance.
1233
+ */
1234
+ ComponentResolveStrategy[ComponentResolveStrategy["MatchIdToClassName"] = 1] = "MatchIdToClassName";
1235
+ /**
1236
+ * Perform a fuzzy ID to classname match
1237
+ * case insensitive, mutes symbols
1238
+ * ignores "Component" and "Module" postfixes on class
1239
+ * names
1240
+ */
1241
+ ComponentResolveStrategy[ComponentResolveStrategy["FuzzyIdClassName"] = 2] = "FuzzyIdClassName";
1242
+ /**
1243
+ * Use a user-provided component match function
1244
+ */
1245
+ ComponentResolveStrategy[ComponentResolveStrategy["Custom"] = 3] = "Custom";
1246
+ })(ComponentResolveStrategy || (ComponentResolveStrategy = {}));
1247
+
1248
+ // Monkey-patch the type of these symbols.
1249
+ const $id = Symbol("id");
1250
+ const $group = Symbol("group");
1251
+ const NGX_LAZY_LOADER_CONFIG = new InjectionToken('config');
1252
+ class LazyLoaderService {
1253
+ get err() { return LazyLoaderService.config.logger.err; }
1254
+ get log() { return LazyLoaderService.config.logger.log; }
1255
+ get warn() { return LazyLoaderService.config.logger.warn; }
1256
+ constructor(config = {}) {
1257
+ // Ensure this is singleton and works regardless of special instancing requirements.
1258
+ LazyLoaderService.configure(config);
1259
+ }
1260
+ static configure(config) {
1261
+ const { log, warn, err } = Logger("ngx-lazy-loader", "#009688");
1262
+ this.config = {
1263
+ componentResolveStrategy: ComponentResolveStrategy.PickFirst,
1264
+ logger: {
1265
+ log,
1266
+ warn,
1267
+ err
1268
+ },
1269
+ ...config
1270
+ };
1271
+ config.entries?.forEach(e => this.addComponentToRegistry(e));
1272
+ // If a custom resolution strategy is provided but no resolution function is passed,
1273
+ // we throw an error
1274
+ if (this.config.componentResolveStrategy == ComponentResolveStrategy.Custom &&
1275
+ !this.config.customResolver) {
1276
+ throw new Error("Cannot initialize. Configuration specifies a custom resolve matcher but none was provided");
1277
+ }
1278
+ if (this.config.loaderDistractorComponent && this.config.loaderDistractorTemplate)
1279
+ throw new Error("Cannot have both a Component and Template for Distractor view.");
1280
+ if (this.config.errorComponent && this.config.errorTemplate)
1281
+ throw new Error("Cannot have both a Component and Template for Error view.");
1282
+ if (this.config.notFoundComponent && this.config.notFoundTemplate)
1283
+ throw new Error("Cannot have both a Component and Template for NotFound view.");
1284
+ }
1285
+ static addComponentToRegistry(registration) {
1286
+ if (!registration)
1287
+ throw new Error("Cannot add <undefined> component into registry.");
1288
+ // Clone the object into our repository and transfer the id into a standardized slug format
1289
+ const id = stringToSlug(registration.id ?? Date.now().toString()); // purge non-basic ASCII chars
1290
+ const group = registration.group || "default";
1291
+ registration[$id] = id;
1292
+ registration[$group] = id;
1293
+ if (!this.registry[group])
1294
+ this.registry[group] = [];
1295
+ // Check if we already have a registration for the component
1296
+ // if (this.registry[group] && typeof this.registry[group]['load'] == "function") {
1297
+ // // Warn the developer that the state is problematic
1298
+ // this.config.logger.warn(
1299
+ // `A previous entry already exists for ${id}! The old registration will be overridden.` +
1300
+ // `Please ensure you use groups if you intend to have duplicate component ids. ` +
1301
+ // `If this was intentional, first remove the old component from the registry before adding a new instance`
1302
+ // );
1303
+ // // If we're in dev mode, break the loader surface
1304
+ // if (isDevMode())
1305
+ // return;
1306
+ // }
1307
+ this.registry[group].push(registration);
1308
+ }
1309
+ /**
1310
+ * Register an Angular component
1311
+ * @param id identifier that is used to resolve the component
1312
+ * @param group
1313
+ * @param component Angular Component Class constructor
1314
+ */
1315
+ registerComponent(args) {
1316
+ if (this.isComponentRegistered(args.id, args.group)) {
1317
+ this.log(`Will not re-register component '${args.id}' in group '${args.group || 'default'}' `);
1318
+ return;
1319
+ }
1320
+ LazyLoaderService.addComponentToRegistry({
1321
+ id: stringToSlug(args.id),
1322
+ matcher: args.matcher,
1323
+ group: stringToSlug(args.group || "default"),
1324
+ load: args.load || (() => args.component)
1325
+ });
1326
+ }
1327
+ /**
1328
+ *
1329
+ * @param id
1330
+ * @param group
1331
+ */
1332
+ unregisterComponent(id, group = "default") {
1333
+ const _id = stringToSlug(id);
1334
+ const _group = stringToSlug(group);
1335
+ if (!this.resolveRegistrationEntry(id, group))
1336
+ throw new Error("Cannot unregister component ${}! Component is not present in registry");
1337
+ // TODO: handle clearing running instances
1338
+ delete LazyLoaderService.registry[_group][_id];
1339
+ }
1340
+ /**
1341
+ * Get the registration entry for a component.
1342
+ * Returns null if component is not in the registry.
1343
+ */
1344
+ resolveRegistrationEntry(value, group = "default") {
1345
+ const _id = stringToSlug(value);
1346
+ const _group = stringToSlug(group);
1347
+ const targetGroup = (LazyLoaderService.registry[_group] || []);
1348
+ let items = targetGroup.filter(t => {
1349
+ if (!t)
1350
+ return false;
1351
+ // No matcher, check id
1352
+ if (!t.matcher)
1353
+ return t[$id] == _id;
1354
+ // Matcher is regex
1355
+ if (t.matcher instanceof RegExp)
1356
+ return t.matcher.test(_id) || t.matcher.test(value);
1357
+ // Matcher is string => regex
1358
+ if (typeof t.matcher == 'string') {
1359
+ const rx = new RegExp(t.matcher, 'ui');
1360
+ return rx.test(_id) || rx.test(value);
1361
+ }
1362
+ // Matcher is array
1363
+ if (Array.isArray(t.matcher)) {
1364
+ return !!t.matcher.find(e => stringToSlug(e) == _id);
1365
+ }
1366
+ // Custom matcher function
1367
+ if (typeof t.matcher == "function")
1368
+ return t.matcher(_id);
1369
+ return false;
1370
+ });
1371
+ if (items.length > 1) {
1372
+ this.warn("Resolved multiple components for the provided `[component]` binding. This may cause UI conflicts.");
1373
+ }
1374
+ if (items.length == 0) {
1375
+ return null;
1376
+ }
1377
+ const out = items[0];
1378
+ if (out.matcher instanceof RegExp) {
1379
+ const result = _id.match(out.matcher) || value.match(out.matcher);
1380
+ return {
1381
+ entry: out,
1382
+ matchGroups: result?.groups
1383
+ };
1384
+ }
1385
+ return { entry: out };
1386
+ }
1387
+ /**
1388
+ * Check if a component is currently registered
1389
+ * Can be used to validate regex matchers and aliases.
1390
+ */
1391
+ isComponentRegistered(value, group = "default") {
1392
+ return !!this.resolveRegistrationEntry(value, group);
1393
+ }
1394
+ /**
1395
+ *
1396
+ * @param bundle
1397
+ * @returns The component `Object` if a component was resolved, `null` if no component was found
1398
+ * `false` if the specified strategy was an invalid selection
1399
+ */
1400
+ resolveComponent(id, group, modules) {
1401
+ switch (LazyLoaderService.config.componentResolveStrategy) {
1402
+ case ComponentResolveStrategy.PickFirst: {
1403
+ return modules[0];
1404
+ }
1405
+ // Exact id -> classname match
1406
+ case ComponentResolveStrategy.MatchIdToClassName: {
1407
+ const matches = modules
1408
+ .filter(k => k.name == id);
1409
+ if (matches.length == 0)
1410
+ return null;
1411
+ return matches[0];
1412
+ }
1413
+ // Fuzzy id -> classname match
1414
+ case ComponentResolveStrategy.FuzzyIdClassName: {
1415
+ const _id = id.replace(/[^a-z0-9_\-]/ig, '');
1416
+ if (_id.length == 0) {
1417
+ LazyLoaderService.config.logger.err("Fuzzy classname matching stripped all symbols from the ID specified!");
1418
+ return false;
1419
+ }
1420
+ const rx = new RegExp(`^${id}(component|module)?$`, "i");
1421
+ const matches = modules
1422
+ .filter(mod => {
1423
+ let kid = mod.name.replace(/[^a-z0-9_\-]/ig, '');
1424
+ return rx.test(kid);
1425
+ });
1426
+ if (matches.length > 1) {
1427
+ LazyLoaderService.config.logger.err("Fuzzy classname matching resolved multiple targets!");
1428
+ return false;
1429
+ }
1430
+ if (matches.length == 0) {
1431
+ LazyLoaderService.config.logger.err("Fuzzy classname matching resolved no targets!");
1432
+ return null;
1433
+ }
1434
+ return matches[0];
1435
+ }
1436
+ case ComponentResolveStrategy.Custom: {
1437
+ return LazyLoaderService.config.customResolver(modules);
1438
+ }
1439
+ default: {
1440
+ return false;
1441
+ }
1442
+ }
1443
+ }
1444
+ }
1445
+ // A proxied registry that mutates reference keys
1446
+ LazyLoaderService.registry = {};
1447
+ LazyLoaderService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderService, deps: [{ token: NGX_LAZY_LOADER_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable });
1448
+ LazyLoaderService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderService, providedIn: 'root' });
1449
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderService, decorators: [{
1450
+ type: Injectable,
1451
+ args: [{
1452
+ providedIn: 'root'
1453
+ }]
1454
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
1455
+ type: Inject,
1456
+ args: [NGX_LAZY_LOADER_CONFIG]
1457
+ }] }]; } });
1458
+
1459
+ class LazyLoaderComponent {
1460
+ /**
1461
+ * The id of the component that will be lazy loaded
1462
+ */
1463
+ set id(data) {
1464
+ const id = stringToSlug(data);
1465
+ // Check if there is a change to the loaded component's id
1466
+ // if it's updated, we destroy and rehydrate the entire container
1467
+ if (this.initialized && this._id != id) {
1468
+ this._id = id;
1469
+ this.ngAfterViewInit();
1470
+ }
1471
+ else {
1472
+ this._id = id;
1473
+ }
1474
+ }
1475
+ ;
1476
+ set group(data) {
1477
+ const group = stringToSlug(data);
1478
+ if (typeof group != "string" || !group)
1479
+ return;
1480
+ // If the group was updated, retry to bootstrap something into the container.
1481
+ if (this.initialized && this._group != group) {
1482
+ this._group = group;
1483
+ this.ngAfterViewInit();
1484
+ return;
1485
+ }
1486
+ this._group = group;
1487
+ }
1488
+ get group() { return this._group; }
1489
+ /**
1490
+ * A map of inputs to bind to the child.
1491
+ * Supports change detection. (May fail on deep JSON changes)
1492
+ *
1493
+ * ```html
1494
+ * <lazy-loader component="MyLazyComponent"
1495
+ * [inputs]="{
1496
+ * prop1: true,
1497
+ * prop2: false,
1498
+ * complex: {
1499
+ * a: true,
1500
+ * b: 0
1501
+ * }
1502
+ * }"
1503
+ * >
1504
+ * </lazy-loader>
1505
+ * ```
1506
+ */
1507
+ set inputs(data) {
1508
+ if (data == undefined)
1509
+ return;
1510
+ let previous = this._inputs;
1511
+ this._inputs = data;
1512
+ if (data == undefined)
1513
+ console.trace(data);
1514
+ if (this.targetComponentFactory) {
1515
+ const { inputs } = this.targetComponentFactory.ɵcmp;
1516
+ const currentKeys = Object.keys(inputs);
1517
+ const oldKeys = Object.keys(previous).filter(key => currentKeys.includes(key));
1518
+ const newKeys = Object.keys(data).filter(key => currentKeys.includes(key));
1519
+ const removed = oldKeys.filter(key => !newKeys.includes(key));
1520
+ // ? perhaps set to null or undefined instead
1521
+ removed.forEach(k => this.targetComponentInstance[k] = null);
1522
+ this.bindInputs();
1523
+ }
1524
+ }
1525
+ /**
1526
+ * A map of outputs to bind from the child.
1527
+ * Should support change detection.
1528
+ * ```html
1529
+ * <lazy-loader component="MyLazyComponent"
1530
+ * [outputs]="{
1531
+ * prop3: onOutputFire
1532
+ * }"
1533
+ * >
1534
+ * </lazy-loader>
1535
+ * ```
1536
+ */
1537
+ set outputs(data) {
1538
+ let previous = this._outputs;
1539
+ this._outputs = data;
1540
+ if (this.targetComponentFactory) {
1541
+ const { inputs } = this.targetComponentFactory.ɵcmp;
1542
+ const currentKeys = Object.keys(inputs);
1543
+ const removed = Object.keys(previous).filter(key => !currentKeys.includes(key));
1544
+ removed.forEach(k => {
1545
+ // Unsubscribe from observable
1546
+ this.outputSubscriptions[k]?.unsubscribe();
1547
+ delete this.targetComponentInstance[k];
1548
+ });
1549
+ this.bindOutputs();
1550
+ }
1551
+ }
1552
+ constructor(service, viewContainerRef, dialog, dialogArguments) {
1553
+ this.service = service;
1554
+ this.viewContainerRef = viewContainerRef;
1555
+ this.dialog = dialog;
1556
+ this.dialogArguments = dialogArguments;
1557
+ this._group = "default";
1558
+ this.outputSubscriptions = {};
1559
+ /**
1560
+ * Emits errors encountered when loading components
1561
+ */
1562
+ this.componentLoadError = new EventEmitter();
1563
+ /**
1564
+ * Emits when the component is fully constructed
1565
+ * and had it's inputs and outputs bound
1566
+ * > before `OnInit`
1567
+ *
1568
+ * Returns the active class instance of the lazy-loaded component
1569
+ */
1570
+ this.componentLoaded = new EventEmitter();
1571
+ // Force 500ms delay before revealing the spinner
1572
+ this.loaderEmitter = new EventEmitter();
1573
+ this.clearLoader$ = this.loaderEmitter.pipe(debounceTime(300));
1574
+ this.showLoader = true; // whether we render the DOM for the spinner
1575
+ this.isClearingLoader = false; // should the spinner start fading out
1576
+ this.initialized = false;
1577
+ this.config = LazyLoaderService.config;
1578
+ this.err = LazyLoaderService.config.logger.err;
1579
+ this.warn = LazyLoaderService.config.logger.warn;
1580
+ this.log = LazyLoaderService.config.logger.log;
1581
+ // First, check for dialog arguments
1582
+ if (this.dialogArguments) {
1583
+ this.inputs = this.dialogArguments.inputs || this.dialogArguments.data;
1584
+ this.outputs = this.dialogArguments.outputs;
1585
+ this.id = this.dialogArguments.id;
1586
+ this.group = this.dialogArguments.group;
1587
+ }
1588
+ this.loaderSub = this.clearLoader$.subscribe(() => {
1589
+ this.showLoader = false;
1590
+ });
1591
+ }
1592
+ async ngAfterViewInit() {
1593
+ this.ngOnDestroy(false);
1594
+ this.isClearingLoader = false;
1595
+ this.showLoader = true;
1596
+ this.initialized = true;
1597
+ if (!this._id) {
1598
+ this.warn("No component was specified!");
1599
+ return this.loadDefault();
1600
+ }
1601
+ try {
1602
+ const _entry = this.service.resolveRegistrationEntry(this._id, this._group);
1603
+ if (!_entry || !_entry.entry) {
1604
+ this.err(`Failed to find Component '${this._id}' in group '${this._group}' in registry!`);
1605
+ return this.loadDefault();
1606
+ }
1607
+ const { entry, matchGroups } = _entry;
1608
+ this._matchGroups = matchGroups;
1609
+ // Download the "module" (the standalone component)
1610
+ const bundle = this.targetModule = await entry.load();
1611
+ // Check if there is some corruption on the bundle.
1612
+ if (!bundle || typeof bundle != 'object' || bundle['__esModule'] !== true || bundle.toString() != "[object Module]") {
1613
+ this.err(`Failed to load component/module for '${this._id}'! Parsed resource is invalid.`);
1614
+ return this.loadError();
1615
+ }
1616
+ const modules = Object.keys(bundle)
1617
+ .map(k => {
1618
+ const entry = bundle[k];
1619
+ // Strictly check for exported modules or standalone components
1620
+ if (typeof entry == "function" && typeof entry["ɵfac"] == "function")
1621
+ return entry;
1622
+ return null;
1623
+ })
1624
+ .filter(e => e != null)
1625
+ .filter(entry => {
1626
+ entry['_isModule'] = !!entry['ɵmod']; // module
1627
+ entry['_isComponent'] = !!entry['ɵcmp']; // component
1628
+ return (entry['_isModule'] || entry['_isComponent']);
1629
+ });
1630
+ if (modules.length == 0) {
1631
+ this.err(`Component/Module loaded for '${this._id}' has no exported components or modules!`);
1632
+ return this.loadError();
1633
+ }
1634
+ const component = this.targetComponentFactory = this.service.resolveComponent(this._id, "default", modules);
1635
+ if (!component) {
1636
+ this.err(`Component '${this._id}' is invalid or corrupted!`);
1637
+ return this.loadError();
1638
+ }
1639
+ // Bootstrap the component into the container
1640
+ const componentRef = this.targetComponentContainerRef = this.targetContainer.createComponent(component);
1641
+ this.targetRef = this.targetContainer.insert(this.targetComponentContainerRef.hostView);
1642
+ const instance = this.targetComponentInstance = componentRef['instance'];
1643
+ this.bindInputs();
1644
+ this.bindOutputs();
1645
+ this.componentLoaded.next(instance);
1646
+ this.instance = instance;
1647
+ // Look for an observable called isLoading$ that will make us show/hide
1648
+ // the same distractor that is used on basic loading
1649
+ const isLoading$ = instance['ngxShowDistractor$'];
1650
+ if (isLoading$ && typeof isLoading$.subscribe == "function") {
1651
+ this.distractorSubscription = isLoading$.subscribe(loading => {
1652
+ if (!loading) {
1653
+ this.isClearingLoader = true;
1654
+ this.loaderEmitter.emit();
1655
+ }
1656
+ else {
1657
+ this.showLoader = true;
1658
+ this.isClearingLoader = false;
1659
+ }
1660
+ });
1661
+ }
1662
+ else {
1663
+ this.isClearingLoader = true;
1664
+ }
1665
+ const name = Object.keys(bundle)[0];
1666
+ this.log(`Loaded '${name}'`);
1667
+ this.loaderEmitter.emit();
1668
+ return componentRef;
1669
+ }
1670
+ catch (ex) {
1671
+ if (isDevMode()) {
1672
+ console.warn("Component " + this._id + " threw an error on mount!");
1673
+ console.warn("This will cause you to see a 404 panel.");
1674
+ console.error(ex);
1675
+ }
1676
+ // Network errors throw a toast and return an error component
1677
+ if (ex && !isDevMode()) {
1678
+ console.error("Uncaught error when loading component");
1679
+ throw ex;
1680
+ }
1681
+ return this.loadDefault();
1682
+ }
1683
+ }
1684
+ ngOnDestroy(clearAll = true) {
1685
+ // unsubscribe from all subscriptions
1686
+ Object.entries(this.outputSubscriptions).forEach(([key, sub]) => {
1687
+ sub.unsubscribe();
1688
+ });
1689
+ this.outputSubscriptions = {};
1690
+ // Clear all things
1691
+ if (clearAll) {
1692
+ this.loaderSub?.unsubscribe();
1693
+ }
1694
+ this.distractorSubscription?.unsubscribe();
1695
+ // Clear target container
1696
+ this.targetRef?.destroy();
1697
+ this.targetComponentContainerRef?.destroy();
1698
+ this.targetContainer?.clear();
1699
+ // Wipe the rest of the state clean
1700
+ this.targetRef = null;
1701
+ this.targetComponentContainerRef = null;
1702
+ }
1703
+ /**
1704
+ * Bind the input values to the child component.
1705
+ */
1706
+ bindInputs() {
1707
+ if (!this._inputs || !this.targetComponentInstance)
1708
+ return;
1709
+ // Merge match groups
1710
+ if (typeof this._matchGroups == "object") {
1711
+ Object.entries(this._matchGroups).forEach(([key, val]) => {
1712
+ if (typeof this._inputs[key] == 'undefined')
1713
+ this._inputs[key] = val;
1714
+ });
1715
+ }
1716
+ // forward-bind inputs
1717
+ const { inputs } = this.targetComponentFactory.ɵcmp;
1718
+ // Returns a list of entries that need to be set
1719
+ // This makes it so that unnecessary setters are not invoked.
1720
+ const updated = Object.entries(inputs).filter(([parentKey, childKey]) => {
1721
+ return this.targetComponentInstance[childKey] != this._inputs[parentKey];
1722
+ });
1723
+ updated.forEach(([parentKey, childKey]) => {
1724
+ if (this._inputs.hasOwnProperty(parentKey))
1725
+ this.targetComponentInstance[childKey] = this._inputs[parentKey];
1726
+ });
1727
+ }
1728
+ /**
1729
+ * Bind the output handlers to the loaded child component
1730
+ */
1731
+ bindOutputs() {
1732
+ if (!this._outputs || !this.targetComponentInstance)
1733
+ return;
1734
+ const { outputs } = this.targetComponentFactory.ɵcmp;
1735
+ // Get a list of unregistered outputs
1736
+ const newOutputs = Object.entries(outputs).filter(([parentKey, childKey]) => {
1737
+ return !this.outputSubscriptions[parentKey];
1738
+ });
1739
+ // Reverse bind via subscription
1740
+ newOutputs.forEach(([parentKey, childKey]) => {
1741
+ if (this._outputs.hasOwnProperty(parentKey)) {
1742
+ const target = this.targetComponentInstance[childKey];
1743
+ const outputs = this._outputs;
1744
+ // Angular folks, stop making this so difficult.
1745
+ const ctx = this.viewContainerRef['_hostLView'][8];
1746
+ const sub = target.subscribe(outputs[parentKey].bind(ctx)); // Subscription
1747
+ this.outputSubscriptions[parentKey] = sub;
1748
+ }
1749
+ });
1750
+ }
1751
+ /**
1752
+ * Load the "Default" component (404) screen normally.
1753
+ * This is shown when the component id isn't in the
1754
+ * registry or otherwise doesn't match
1755
+ *
1756
+ * This
1757
+ */
1758
+ loadDefault() {
1759
+ if (this.config.notFoundComponent)
1760
+ this.targetContainer.createComponent(this.config.notFoundComponent);
1761
+ this.showLoader = false;
1762
+ }
1763
+ /**
1764
+ * Load the "Error" component.
1765
+ * This is shown when we are able to resolve the component
1766
+ * in the registry, but have some issue boostrapping the
1767
+ * component into the viewContainer
1768
+ */
1769
+ loadError() {
1770
+ if (this.config.errorComponent)
1771
+ this.targetContainer.createComponent(this.config.errorComponent);
1772
+ this.showLoader = false;
1773
+ }
1774
+ }
1775
+ LazyLoaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderComponent, deps: [{ token: LazyLoaderService }, { token: i0.ViewContainerRef, optional: true }, { token: i2$2.DialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
1776
+ LazyLoaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: LazyLoaderComponent, isStandalone: true, selector: "ngx-lazy-loader", inputs: { id: ["component", "id"], group: "group", inputs: "inputs", outputs: "outputs" }, outputs: { componentLoadError: "componentLoadError", componentLoaded: "componentLoaded" }, viewQueries: [{ propertyName: "targetContainer", first: true, predicate: ["content"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<ng-container #content></ng-container>\n\n<div class=\"ngx-lazy-loader-distractor\" [class.destroying]=\"isClearingLoader\">\n <ng-container *ngIf=\"config.loaderDistractorComponent\" [ngComponentOutlet]=\"config.loaderDistractorComponent\"></ng-container>\n <ng-container *ngIf=\"config.loaderDistractorTemplate\" [ngTemplateOutlet]=\"config.loaderDistractorTemplate\" [ngTemplateOutletContext]=\"{ '$implicit': inputs }\"></ng-container>\n</div>\n", styles: [":host{display:contents;contain:content;z-index:1;position:relative}.ngx-lazy-loader-distractor{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;background-color:var(--background-color, #212121);opacity:1;transition:opacity .3s ease;z-index:999999}.ngx-lazy-loader-distractor.destroying{opacity:0;pointer-events:none}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
1777
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderComponent, decorators: [{
1778
+ type: Component,
1779
+ args: [{ selector: 'ngx-lazy-loader', imports: [NgIf, NgComponentOutlet, NgTemplateOutlet], standalone: true, template: "<ng-container #content></ng-container>\n\n<div class=\"ngx-lazy-loader-distractor\" [class.destroying]=\"isClearingLoader\">\n <ng-container *ngIf=\"config.loaderDistractorComponent\" [ngComponentOutlet]=\"config.loaderDistractorComponent\"></ng-container>\n <ng-container *ngIf=\"config.loaderDistractorTemplate\" [ngTemplateOutlet]=\"config.loaderDistractorTemplate\" [ngTemplateOutletContext]=\"{ '$implicit': inputs }\"></ng-container>\n</div>\n", styles: [":host{display:contents;contain:content;z-index:1;position:relative}.ngx-lazy-loader-distractor{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;flex-direction:column;background-color:var(--background-color, #212121);opacity:1;transition:opacity .3s ease;z-index:999999}.ngx-lazy-loader-distractor.destroying{opacity:0;pointer-events:none}\n"] }]
1780
+ }], ctorParameters: function () { return [{ type: LazyLoaderService }, { type: i0.ViewContainerRef, decorators: [{
1781
+ type: Optional
1782
+ }] }, { type: i2$2.DialogRef, decorators: [{
1783
+ type: Optional
1784
+ }] }, { type: undefined, decorators: [{
1785
+ type: Optional
1786
+ }, {
1787
+ type: Inject,
1788
+ args: [MAT_DIALOG_DATA]
1789
+ }] }]; }, propDecorators: { targetContainer: [{
1790
+ type: ViewChild,
1791
+ args: ["content", { read: ViewContainerRef }]
1792
+ }], id: [{
1793
+ type: Input,
1794
+ args: ["component"]
1795
+ }], group: [{
1796
+ type: Input,
1797
+ args: ["group"]
1798
+ }], inputs: [{
1799
+ type: Input,
1800
+ args: ["inputs"]
1801
+ }], outputs: [{
1802
+ type: Input,
1803
+ args: ["outputs"]
1804
+ }], componentLoadError: [{
1805
+ type: Output
1806
+ }], componentLoaded: [{
1807
+ type: Output
1808
+ }] } });
1809
+
1810
+ class LazyLoaderModule {
1811
+ static forRoot(config) {
1812
+ return ({
1813
+ ngModule: LazyLoaderModule,
1814
+ providers: [
1815
+ LazyLoaderService,
1816
+ {
1817
+ provide: NGX_LAZY_LOADER_CONFIG,
1818
+ useValue: config
1819
+ }
1820
+ ]
1821
+ });
1822
+ }
1823
+ }
1824
+ LazyLoaderModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1825
+ LazyLoaderModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderModule, imports: [LazyLoaderComponent], exports: [LazyLoaderComponent] });
1826
+ LazyLoaderModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderModule, imports: [LazyLoaderComponent] });
1827
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: LazyLoaderModule, decorators: [{
1828
+ type: NgModule,
1829
+ args: [{
1830
+ imports: [LazyLoaderComponent],
1831
+ exports: [LazyLoaderComponent]
1832
+ }]
1833
+ }] });
1834
+
1835
+ class OnMount {
1836
+ }
1837
+ class DynamicHTMLOptions {
1838
+ }
1839
+ const NGX_DYNAMIC_CONFIG = new InjectionToken('config');
1840
+
1841
+ function isBrowserPlatform() {
1842
+ return window != null && window.document != null;
1843
+ }
1844
+ class DynamicHTMLRenderer {
1845
+ constructor(config, cfr, injector) {
1846
+ this.config = config;
1847
+ this.cfr = cfr;
1848
+ this.injector = injector;
1849
+ this.componentFactories = new Map();
1850
+ this.componentRefs = new Map();
1851
+ this.config.components.forEach(({ selector, component }) => {
1852
+ let cf;
1853
+ cf = this.cfr.resolveComponentFactory(component);
1854
+ this.componentFactories.set(selector, cf);
1855
+ });
1856
+ }
1857
+ renderInnerHTML(elementRef, html) {
1858
+ if (!isBrowserPlatform()) {
1859
+ return {
1860
+ check: () => { },
1861
+ destroy: () => { },
1862
+ };
1863
+ }
1864
+ elementRef.nativeElement.innerHTML = html;
1865
+ const componentRefs = [];
1866
+ this.config.components.forEach(({ selector }) => {
1867
+ const elements = elementRef.nativeElement.querySelectorAll(selector);
1868
+ Array.prototype.forEach.call(elements, (el) => {
1869
+ const content = el.innerHTML;
1870
+ const cmpRef = this.componentFactories.get(selector).create(this.injector, [], el);
1871
+ el.removeAttribute('ng-version');
1872
+ if (cmpRef.instance.dynamicOnMount) {
1873
+ const attrsMap = new Map();
1874
+ if (el.hasAttributes()) {
1875
+ Array.prototype.forEach.call(el.attributes, (attr) => {
1876
+ attrsMap.set(attr.name, attr.value);
1877
+ });
1878
+ }
1879
+ cmpRef.instance.dynamicOnMount(attrsMap, content, el);
1880
+ }
1881
+ componentRefs.push(cmpRef);
1882
+ });
1883
+ });
1884
+ this.componentRefs.set(elementRef, componentRefs);
1885
+ return {
1886
+ check: () => componentRefs.forEach(ref => ref.changeDetectorRef.detectChanges()),
1887
+ destroy: () => {
1888
+ componentRefs.forEach(ref => ref.destroy());
1889
+ this.componentRefs.delete(elementRef);
1890
+ },
1891
+ };
1892
+ }
1893
+ }
1894
+ DynamicHTMLRenderer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DynamicHTMLRenderer, deps: [{ token: NGX_DYNAMIC_CONFIG }, { token: i0.ComponentFactoryResolver }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1895
+ DynamicHTMLRenderer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DynamicHTMLRenderer });
1896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DynamicHTMLRenderer, decorators: [{
1897
+ type: Injectable
1898
+ }], ctorParameters: function () { return [{ type: DynamicHTMLOptions, decorators: [{
1899
+ type: Inject,
1900
+ args: [NGX_DYNAMIC_CONFIG]
1901
+ }] }, { type: i0.ComponentFactoryResolver }, { type: i0.Injector }]; } });
1902
+
1903
+ class DynamicHTMLComponent {
1904
+ constructor(renderer, elementRef) {
1905
+ this.renderer = renderer;
1906
+ this.elementRef = elementRef;
1907
+ this.ref = null;
1908
+ }
1909
+ ngOnChanges(_) {
1910
+ if (this.ref) {
1911
+ this.ref.destroy();
1912
+ this.ref = null;
1913
+ }
1914
+ if (this.content && this.elementRef) {
1915
+ this.ref = this.renderer.renderInnerHTML(this.elementRef, this.content);
1916
+ }
1917
+ }
1918
+ ngDoCheck() {
1919
+ if (this.ref) {
1920
+ this.ref.check();
1921
+ }
1922
+ }
1923
+ ngOnDestroy() {
1924
+ if (this.ref) {
1925
+ this.ref.destroy();
1926
+ this.ref = null;
1927
+ }
1928
+ }
1929
+ }
1930
+ DynamicHTMLComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DynamicHTMLComponent, deps: [{ token: DynamicHTMLRenderer }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1931
+ DynamicHTMLComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: DynamicHTMLComponent, isStandalone: true, selector: "dynamic-html", inputs: { content: "content" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true });
1932
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: DynamicHTMLComponent, decorators: [{
1933
+ type: Component,
1934
+ args: [{
1935
+ selector: 'dynamic-html',
1936
+ template: '',
1937
+ standalone: true
1938
+ }]
1939
+ }], ctorParameters: function () { return [{ type: DynamicHTMLRenderer }, { type: i0.ElementRef }]; }, propDecorators: { content: [{
1940
+ type: Input
1941
+ }] } });
1942
+
1943
+ class NgxDynamicHTMLModule {
1944
+ static forRoot(config) {
1945
+ return {
1946
+ ngModule: NgxDynamicHTMLModule,
1947
+ providers: [
1948
+ DynamicHTMLRenderer,
1949
+ { provide: NGX_DYNAMIC_CONFIG, useValue: config }
1950
+ ],
1951
+ };
1952
+ }
1953
+ }
1954
+ NgxDynamicHTMLModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgxDynamicHTMLModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1955
+ NgxDynamicHTMLModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: NgxDynamicHTMLModule, imports: [DynamicHTMLComponent], exports: [DynamicHTMLComponent] });
1956
+ NgxDynamicHTMLModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgxDynamicHTMLModule, imports: [DynamicHTMLComponent] });
1957
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: NgxDynamicHTMLModule, decorators: [{
1958
+ type: NgModule,
1959
+ args: [{
1960
+ imports: [DynamicHTMLComponent],
1961
+ exports: [DynamicHTMLComponent],
1962
+ }]
1963
+ }] });
1964
+
1965
+ /*
1966
+ * Public API Surface of package
1967
+ */
1968
+ /**
1969
+ ** Directives
1970
+ */
1971
+
1972
+ /**
1973
+ * Generated bundle index. Do not edit.
1974
+ */
1975
+
1976
+ export { ComponentResolveStrategy, DependencyService, DialogService, DynamicHTMLComponent, DynamicHTMLOptions, DynamicHTMLRenderer, Fetch, HtmlBypass, KeyboardService, LazyLoaderComponent, LazyLoaderModule, LazyLoaderService, MenuDirective, NGX_DYNAMIC_CONFIG, NGX_LAZY_LOADER_CONFIG, NgxDynamicHTMLModule, OnMount, ResourceBypass, ScriptBypass, StyleBypass, TooltipDirective, UrlBypass, openMenu, openTooltip };
1977
+ //# sourceMappingURL=dotglitch-ngx-common.mjs.map