UNPKG

13.8 kBJavaScriptView Raw
1import { EventEmitter, Injector, ElementRef, TemplateRef, ɵɵdefineInjectable, ɵɵinject, ComponentFactoryResolver, NgZone, INJECTOR, ApplicationRef, Injectable } from '@angular/core';
2import { listenToTriggersV2, registerOutsideClick, registerEscClick } from 'ngx-bootstrap/utils';
3import { PositioningService } from 'ngx-bootstrap/positioning';
4
5class BsComponentRef {
6}
7
8/**
9 * @copyright Valor Software
10 * @copyright Angular ng-bootstrap team
11 */
12class ContentRef {
13 constructor(
14 // eslint-disable-next-line @typescript-eslint/no-explicit-any
15 nodes, viewRef,
16 // eslint-disable-next-line @typescript-eslint/no-explicit-any
17 componentRef) {
18 this.nodes = nodes;
19 this.viewRef = viewRef;
20 this.componentRef = componentRef;
21 }
22}
23
24// todo: add delay support
25class ComponentLoader {
26 /**
27 * Do not use this directly, it should be instanced via
28 * `ComponentLoadFactory.attach`
29 * @internal
30 */
31 constructor(_viewContainerRef, _renderer, _elementRef, _injector, _componentFactoryResolver, _ngZone, _applicationRef, _posService) {
32 this._viewContainerRef = _viewContainerRef;
33 this._renderer = _renderer;
34 this._elementRef = _elementRef;
35 this._injector = _injector;
36 this._componentFactoryResolver = _componentFactoryResolver;
37 this._ngZone = _ngZone;
38 this._applicationRef = _applicationRef;
39 this._posService = _posService;
40 this.onBeforeShow = new EventEmitter();
41 this.onShown = new EventEmitter();
42 this.onBeforeHide = new EventEmitter();
43 this.onHidden = new EventEmitter();
44 this._providers = [];
45 this._isHiding = false;
46 /**
47 * A selector used if container element was not found
48 */
49 this.containerDefaultSelector = 'body';
50 this._listenOpts = {};
51 this._globalListener = Function.prototype;
52 }
53 get isShown() {
54 if (this._isHiding) {
55 return false;
56 }
57 return !!this._componentRef;
58 }
59 attach(compType) {
60 this._componentFactory = this._componentFactoryResolver
61 .resolveComponentFactory(compType);
62 return this;
63 }
64 // todo: add behaviour: to target element, `body`, custom element
65 to(container) {
66 this.container = container || this.container;
67 return this;
68 }
69 position(opts) {
70 if (!opts) {
71 return this;
72 }
73 this.attachment = opts.attachment || this.attachment;
74 this._elementRef = opts.target || this._elementRef;
75 return this;
76 }
77 provide(provider) {
78 this._providers.push(provider);
79 return this;
80 }
81 // todo: appendChild to element or document.querySelector(this.container)
82 show(opts = {}) {
83 this._subscribePositioning();
84 this._innerComponent = void 0;
85 if (!this._componentRef) {
86 this.onBeforeShow.emit();
87 this._contentRef = this._getContentRef(opts.content, opts.context, opts.initialState);
88 const injector = Injector.create({
89 providers: this._providers,
90 parent: this._injector
91 });
92 if (!this._componentFactory) {
93 return;
94 }
95 this._componentRef = this._componentFactory.create(injector, this._contentRef.nodes);
96 this._applicationRef.attachView(this._componentRef.hostView);
97 // this._componentRef = this._viewContainerRef
98 // .createComponent(this._componentFactory, 0, injector, this._contentRef.nodes);
99 this.instance = this._componentRef.instance;
100 Object.assign(this._componentRef.instance, opts);
101 if (this.container instanceof ElementRef) {
102 this.container.nativeElement.appendChild(this._componentRef.location.nativeElement);
103 }
104 if (typeof this.container === 'string' && typeof document !== 'undefined') {
105 const selectedElement = document.querySelector(this.container) ||
106 document.querySelector(this.containerDefaultSelector);
107 if (!selectedElement) {
108 return;
109 }
110 selectedElement.appendChild(this._componentRef.location.nativeElement);
111 }
112 if (!this.container &&
113 this._elementRef &&
114 this._elementRef.nativeElement.parentElement) {
115 this._elementRef.nativeElement.parentElement.appendChild(this._componentRef.location.nativeElement);
116 }
117 // we need to manually invoke change detection since events registered
118 // via
119 // Renderer::listen() are not picked up by change detection with the
120 // OnPush strategy
121 if (this._contentRef.componentRef) {
122 this._innerComponent = this._contentRef.componentRef.instance;
123 this._contentRef.componentRef.changeDetectorRef.markForCheck();
124 this._contentRef.componentRef.changeDetectorRef.detectChanges();
125 }
126 this._componentRef.changeDetectorRef.markForCheck();
127 this._componentRef.changeDetectorRef.detectChanges();
128 this.onShown.emit(opts.id ? { id: opts.id } : this._componentRef.instance);
129 }
130 this._registerOutsideClick();
131 return this._componentRef;
132 }
133 hide(id) {
134 var _a, _b, _c;
135 if (!this._componentRef) {
136 return this;
137 }
138 this._posService.deletePositionElement(this._componentRef.location);
139 this.onBeforeHide.emit(this._componentRef.instance);
140 const componentEl = this._componentRef.location.nativeElement;
141 componentEl.parentNode.removeChild(componentEl);
142 if ((_a = this._contentRef) === null || _a === void 0 ? void 0 : _a.componentRef) {
143 this._contentRef.componentRef.destroy();
144 }
145 if (this._viewContainerRef && ((_b = this._contentRef) === null || _b === void 0 ? void 0 : _b.viewRef)) {
146 this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._contentRef.viewRef));
147 }
148 if ((_c = this._contentRef) === null || _c === void 0 ? void 0 : _c.viewRef) {
149 this._contentRef.viewRef.destroy();
150 }
151 this._contentRef = void 0;
152 this._componentRef = void 0;
153 this._removeGlobalListener();
154 this.onHidden.emit(id ? { id } : null);
155 return this;
156 }
157 toggle() {
158 if (this.isShown) {
159 this.hide();
160 return;
161 }
162 this.show();
163 }
164 dispose() {
165 if (this.isShown) {
166 this.hide();
167 }
168 this._unsubscribePositioning();
169 if (this._unregisterListenersFn) {
170 this._unregisterListenersFn();
171 }
172 }
173 listen(listenOpts) {
174 var _a;
175 this.triggers = listenOpts.triggers || this.triggers;
176 this._listenOpts.outsideClick = listenOpts.outsideClick;
177 this._listenOpts.outsideEsc = listenOpts.outsideEsc;
178 listenOpts.target = listenOpts.target || ((_a = this._elementRef) === null || _a === void 0 ? void 0 : _a.nativeElement);
179 const hide = (this._listenOpts.hide = () => listenOpts.hide ? listenOpts.hide() : void this.hide());
180 const show = (this._listenOpts.show = (registerHide) => {
181 listenOpts.show ? listenOpts.show(registerHide) : this.show(registerHide);
182 registerHide();
183 });
184 // eslint-disable-next-line @typescript-eslint/no-explicit-any
185 const toggle = (registerHide) => {
186 this.isShown ? hide() : show(registerHide);
187 };
188 if (this._renderer) {
189 this._unregisterListenersFn = listenToTriggersV2(this._renderer, {
190 target: listenOpts.target,
191 triggers: listenOpts.triggers,
192 show,
193 hide,
194 toggle
195 });
196 }
197 return this;
198 }
199 _removeGlobalListener() {
200 if (this._globalListener) {
201 this._globalListener();
202 this._globalListener = Function.prototype;
203 }
204 }
205 attachInline(vRef,
206 // eslint-disable-next-line @typescript-eslint/no-explicit-any
207 template) {
208 if (vRef && template) {
209 this._inlineViewRef = vRef.createEmbeddedView(template);
210 }
211 return this;
212 }
213 _registerOutsideClick() {
214 if (!this._componentRef || !this._componentRef.location) {
215 return;
216 }
217 // why: should run after first event bubble
218 if (this._listenOpts.outsideClick) {
219 const target = this._componentRef.location.nativeElement;
220 setTimeout(() => {
221 if (this._renderer && this._elementRef) {
222 this._globalListener = registerOutsideClick(this._renderer, {
223 targets: [target, this._elementRef.nativeElement],
224 outsideClick: this._listenOpts.outsideClick,
225 hide: () => this._listenOpts.hide && this._listenOpts.hide()
226 });
227 }
228 });
229 }
230 if (this._listenOpts.outsideEsc && this._renderer && this._elementRef) {
231 const target = this._componentRef.location.nativeElement;
232 this._globalListener = registerEscClick(this._renderer, {
233 targets: [target, this._elementRef.nativeElement],
234 outsideEsc: this._listenOpts.outsideEsc,
235 hide: () => this._listenOpts.hide && this._listenOpts.hide()
236 });
237 }
238 }
239 getInnerComponent() {
240 return this._innerComponent;
241 }
242 _subscribePositioning() {
243 if (this._zoneSubscription || !this.attachment) {
244 return;
245 }
246 this.onShown.subscribe(() => {
247 var _a;
248 this._posService.position({
249 element: (_a = this._componentRef) === null || _a === void 0 ? void 0 : _a.location,
250 target: this._elementRef,
251 attachment: this.attachment,
252 appendToBody: this.container === 'body'
253 });
254 });
255 this._zoneSubscription = this._ngZone.onStable.subscribe(() => {
256 if (!this._componentRef) {
257 return;
258 }
259 this._posService.calcPosition();
260 });
261 }
262 _unsubscribePositioning() {
263 if (!this._zoneSubscription) {
264 return;
265 }
266 this._zoneSubscription.unsubscribe();
267 this._zoneSubscription = void 0;
268 }
269 _getContentRef(
270 // eslint-disable-next-line @typescript-eslint/no-explicit-any
271 content,
272 // eslint-disable-next-line @typescript-eslint/no-explicit-any
273 context,
274 // eslint-disable-next-line @typescript-eslint/no-explicit-any
275 initialState) {
276 if (!content) {
277 return new ContentRef([]);
278 }
279 if (content instanceof TemplateRef) {
280 if (this._viewContainerRef) {
281 const _viewRef = this._viewContainerRef
282 .createEmbeddedView(content, context);
283 _viewRef.markForCheck();
284 return new ContentRef([_viewRef.rootNodes], _viewRef);
285 }
286 const viewRef = content.createEmbeddedView({});
287 this._applicationRef.attachView(viewRef);
288 return new ContentRef([viewRef.rootNodes], viewRef);
289 }
290 if (typeof content === 'function') {
291 const contentCmptFactory = this._componentFactoryResolver.resolveComponentFactory(content);
292 const modalContentInjector = Injector.create({
293 providers: this._providers,
294 parent: this._injector
295 });
296 const componentRef = contentCmptFactory.create(modalContentInjector);
297 Object.assign(componentRef.instance, initialState);
298 this._applicationRef.attachView(componentRef.hostView);
299 return new ContentRef([[componentRef.location.nativeElement]], componentRef.hostView, componentRef);
300 }
301 const nodes = this._renderer
302 ? [this._renderer.createText(`${content}`)]
303 : [];
304 return new ContentRef([nodes]);
305 }
306}
307
308class ComponentLoaderFactory {
309 constructor(_componentFactoryResolver, _ngZone, _injector, _posService, _applicationRef) {
310 this._componentFactoryResolver = _componentFactoryResolver;
311 this._ngZone = _ngZone;
312 this._injector = _injector;
313 this._posService = _posService;
314 this._applicationRef = _applicationRef;
315 }
316 /**
317 *
318 * @param _elementRef
319 * @param _viewContainerRef
320 * @param _renderer
321 */
322 createLoader(_elementRef, _viewContainerRef, _renderer) {
323 return new ComponentLoader(_viewContainerRef, _renderer, _elementRef, this._injector, this._componentFactoryResolver, this._ngZone, this._applicationRef, this._posService);
324 }
325}
326ComponentLoaderFactory.ɵprov = ɵɵdefineInjectable({ factory: function ComponentLoaderFactory_Factory() { return new ComponentLoaderFactory(ɵɵinject(ComponentFactoryResolver), ɵɵinject(NgZone), ɵɵinject(INJECTOR), ɵɵinject(PositioningService), ɵɵinject(ApplicationRef)); }, token: ComponentLoaderFactory, providedIn: "root" });
327ComponentLoaderFactory.decorators = [
328 { type: Injectable, args: [{ providedIn: 'root' },] }
329];
330ComponentLoaderFactory.ctorParameters = () => [
331 { type: ComponentFactoryResolver },
332 { type: NgZone },
333 { type: Injector },
334 { type: PositioningService },
335 { type: ApplicationRef }
336];
337
338/**
339 * Generated bundle index. Do not edit.
340 */
341
342export { BsComponentRef, ComponentLoader, ComponentLoaderFactory, ContentRef };
343//# sourceMappingURL=ngx-bootstrap-component-loader.js.map