UNPKG

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