@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.
- package/README.md +24 -0
- package/components/dynamic-html/dynamic-html.component.d.ts +15 -0
- package/components/dynamic-html/dynamic-html.module.d.ts +10 -0
- package/components/dynamic-html/dynamic-html.service.d.ts +18 -0
- package/components/dynamic-html/types.d.ts +12 -0
- package/components/lazy-loader/lazy-loader.component.d.ts +146 -0
- package/components/lazy-loader/lazy-loader.module.d.ts +10 -0
- package/components/lazy-loader/lazy-loader.service.d.ts +71 -0
- package/components/lazy-loader/types.d.ts +142 -0
- package/components/menu/menu.component.d.ts +52 -0
- package/components/tooltip/tooltip.component.d.ts +35 -0
- package/directives/menu.directive.d.ts +27 -0
- package/directives/tooltip.directive.d.ts +26 -0
- package/directives/utils.d.ts +8 -0
- package/esm2020/components/dynamic-html/dynamic-html.component.mjs +43 -0
- package/esm2020/components/dynamic-html/dynamic-html.module.mjs +27 -0
- package/esm2020/components/dynamic-html/dynamic-html.service.mjs +66 -0
- package/esm2020/components/dynamic-html/types.mjs +7 -0
- package/esm2020/components/lazy-loader/lazy-loader.component.mjs +360 -0
- package/esm2020/components/lazy-loader/lazy-loader.module.mjs +29 -0
- package/esm2020/components/lazy-loader/lazy-loader.service.mjs +215 -0
- package/esm2020/components/lazy-loader/types.mjs +26 -0
- package/esm2020/components/menu/menu.component.mjs +316 -0
- package/esm2020/components/tooltip/tooltip.component.mjs +135 -0
- package/esm2020/directives/menu.directive.mjs +112 -0
- package/esm2020/directives/tooltip.directive.mjs +92 -0
- package/esm2020/directives/utils.mjs +120 -0
- package/esm2020/dotglitch-ngx-common.mjs +5 -0
- package/esm2020/pipes/html-bypass.pipe.mjs +27 -0
- package/esm2020/pipes/resource-bypass.pipe.mjs +27 -0
- package/esm2020/pipes/script-bypass.pipe.mjs +27 -0
- package/esm2020/pipes/style-bypass.pipe.mjs +27 -0
- package/esm2020/pipes/url-bypass.pipe.mjs +27 -0
- package/esm2020/public-api.mjs +39 -0
- package/esm2020/services/dependency.service.mjs +55 -0
- package/esm2020/services/dialog.service.mjs +66 -0
- package/esm2020/services/fetch.service.mjs +71 -0
- package/esm2020/services/keyboard.service.mjs +128 -0
- package/esm2020/types/menu.mjs +2 -0
- package/esm2020/types/popup.mjs +2 -0
- package/esm2020/utils/index.mjs +39 -0
- package/fesm2015/dotglitch-ngx-common.mjs +1997 -0
- package/fesm2015/dotglitch-ngx-common.mjs.map +1 -0
- package/fesm2020/dotglitch-ngx-common.mjs +1977 -0
- package/fesm2020/dotglitch-ngx-common.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/package.json +60 -0
- package/pipes/html-bypass.pipe.d.ts +16 -0
- package/pipes/resource-bypass.pipe.d.ts +16 -0
- package/pipes/script-bypass.pipe.d.ts +16 -0
- package/pipes/style-bypass.pipe.d.ts +16 -0
- package/pipes/url-bypass.pipe.d.ts +16 -0
- package/public-api.d.ts +34 -0
- package/services/dependency.service.d.ts +19 -0
- package/services/dialog.service.d.ts +41 -0
- package/services/fetch.service.d.ts +28 -0
- package/services/keyboard.service.d.ts +79 -0
- package/types/menu.d.ts +90 -0
- package/types/popup.d.ts +27 -0
- 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
|