1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | import { ChangeDetectorRef, Component, EventEmitter, HostListener, Injectable, Input, NgModule, Output, Renderer2, ViewChild } from '@angular/core';
|
7 | import { CommonModule } from '@angular/common';
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | class NgxSmartModalService {
|
14 | constructor() {
|
15 | this.modalStack = [];
|
16 | }
|
17 | |
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | addModal(modalInstance, force) {
|
26 | if (force) {
|
27 | const i = this.modalStack.findIndex((o) => {
|
28 | return o.id === modalInstance.id;
|
29 | });
|
30 | if (i > -1) {
|
31 | this.modalStack[i].modal = modalInstance.modal;
|
32 | }
|
33 | else {
|
34 | this.modalStack.push(modalInstance);
|
35 | }
|
36 | return;
|
37 | }
|
38 | this.modalStack.push(modalInstance);
|
39 | }
|
40 | |
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | getModal(id) {
|
47 | return this.modalStack.filter((o) => {
|
48 | return o.id === id;
|
49 | })[0].modal;
|
50 | }
|
51 | |
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | get(id) {
|
58 | return this.getModal(id);
|
59 | }
|
60 | |
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | open(id, force = false) {
|
68 | const instance = this.modalStack.find((o) => {
|
69 | return o.id === id;
|
70 | });
|
71 | if (!!instance) {
|
72 | instance.modal.open(force);
|
73 | }
|
74 | else {
|
75 | throw new Error('Modal not found');
|
76 | }
|
77 | }
|
78 | |
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | close(id) {
|
85 | const instance = this.modalStack.find((o) => {
|
86 | return o.id === id;
|
87 | });
|
88 | if (!!instance) {
|
89 | instance.modal.close();
|
90 | }
|
91 | else {
|
92 | throw new Error('Modal not found');
|
93 | }
|
94 | }
|
95 | |
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | toggle(id, force = false) {
|
104 | const instance = this.modalStack.find((o) => {
|
105 | return o.id === id;
|
106 | });
|
107 | if (!!instance) {
|
108 | instance.modal.toggle(force);
|
109 | }
|
110 | else {
|
111 | throw new Error('Modal not found');
|
112 | }
|
113 | }
|
114 | |
115 |
|
116 |
|
117 |
|
118 |
|
119 | getModalStack() {
|
120 | return this.modalStack;
|
121 | }
|
122 | |
123 |
|
124 |
|
125 |
|
126 |
|
127 | getOpenedModals() {
|
128 | const modals = [];
|
129 | this.modalStack.forEach((o) => {
|
130 | if (o.modal.visible) {
|
131 | modals.push(o);
|
132 | }
|
133 | });
|
134 | return modals;
|
135 | }
|
136 | |
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | getHigherIndex() {
|
144 | const index = [1041];
|
145 | const modals = this.getModalStack();
|
146 | modals.forEach((o) => {
|
147 | index.push(o.modal.layerPosition);
|
148 | });
|
149 | return Math.max(...index) + 1;
|
150 | }
|
151 | |
152 |
|
153 |
|
154 |
|
155 |
|
156 | getModalStackCount() {
|
157 | return this.modalStack.length;
|
158 | }
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | removeModal(id) {
|
166 | const i = this.modalStack.findIndex((o) => {
|
167 | return o.id === id;
|
168 | });
|
169 | if (i > -1) {
|
170 | this.modalStack.splice(i, 1);
|
171 | }
|
172 | }
|
173 | |
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | setModalData(data, id, force) {
|
185 | if (!!this.modalStack.find((o) => {
|
186 | return o.id === id;
|
187 | })) {
|
188 | this.getModal(id).setData(data, force);
|
189 | return true;
|
190 | }
|
191 | else {
|
192 | return false;
|
193 | }
|
194 | }
|
195 | |
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | getModalData(id) {
|
202 | return this.getModal(id).getData();
|
203 | }
|
204 | |
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | resetModalData(id) {
|
211 | if (!!this.modalStack.find((o) => {
|
212 | return o.id === id;
|
213 | })) {
|
214 | const removed = this.getModal(id).getData();
|
215 | this.getModal(id).removeData();
|
216 | return removed;
|
217 | }
|
218 | else {
|
219 | return false;
|
220 | }
|
221 | }
|
222 | |
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 | closeLatestModal() {
|
229 | const me = this;
|
230 | clearTimeout(this.debouncer);
|
231 | this.debouncer = setTimeout(() => {
|
232 | let tmp;
|
233 | me.getOpenedModals().forEach((m) => {
|
234 | if (m.modal.layerPosition > (!!tmp ? tmp.modal.layerPosition : 0 && m.modal.escapable)) {
|
235 | tmp = m;
|
236 | }
|
237 | });
|
238 | return !!tmp ? tmp.modal.close() : false;
|
239 | }, 100);
|
240 | }
|
241 | }
|
242 | NgxSmartModalService.decorators = [
|
243 | { type: Injectable },
|
244 | ];
|
245 |
|
246 | NgxSmartModalService.ctorParameters = () => [];
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 | class NgxSmartModalComponent {
|
253 | |
254 |
|
255 |
|
256 |
|
257 |
|
258 | constructor(_renderer, _changeDetectorRef, _ngxSmartModalService) {
|
259 | this._renderer = _renderer;
|
260 | this._changeDetectorRef = _changeDetectorRef;
|
261 | this._ngxSmartModalService = _ngxSmartModalService;
|
262 | this.closable = true;
|
263 | this.escapable = true;
|
264 | this.dismissable = true;
|
265 | this.identifier = '';
|
266 | this.customClass = 'nsm-dialog-animation-fade';
|
267 | this.visible = false;
|
268 | this.backdrop = true;
|
269 | this.force = true;
|
270 | this.hideDelay = 500;
|
271 | this.autostart = false;
|
272 | this.visibleChange = new EventEmitter();
|
273 | this.onClose = new EventEmitter();
|
274 | this.onCloseFinished = new EventEmitter();
|
275 | this.onDismiss = new EventEmitter();
|
276 | this.onDismissFinished = new EventEmitter();
|
277 | this.onAnyCloseEvent = new EventEmitter();
|
278 | this.onAnyCloseEventFinished = new EventEmitter();
|
279 | this.onOpen = new EventEmitter();
|
280 | this.onEscape = new EventEmitter();
|
281 | this.onDataAdded = new EventEmitter();
|
282 | this.onDataRemoved = new EventEmitter();
|
283 | this.layerPosition = 1041;
|
284 | this.overlayVisible = false;
|
285 | this.openedClass = false;
|
286 | this.escapeKeyboardEvent = (event) => {
|
287 | if (event.keyCode === 27) {
|
288 | this.onEscape.emit(this);
|
289 | this._ngxSmartModalService.closeLatestModal();
|
290 | }
|
291 | };
|
292 | }
|
293 | |
294 |
|
295 |
|
296 | ngOnInit() {
|
297 | if (!!this.identifier && this.identifier.length) {
|
298 | this.layerPosition += this._ngxSmartModalService.getModalStackCount();
|
299 | this._ngxSmartModalService.addModal({ id: this.identifier, modal: this }, this.force);
|
300 | if (this.autostart) {
|
301 | this._ngxSmartModalService.open(this.identifier);
|
302 | }
|
303 | }
|
304 | else {
|
305 | throw new Error('identifier field isn’t set. Please set one before calling <ngx-smart-modal> in a template.');
|
306 | }
|
307 | }
|
308 | |
309 |
|
310 |
|
311 | ngOnDestroy() {
|
312 | this._ngxSmartModalService.removeModal(this.identifier);
|
313 | window.removeEventListener('keyup', this.escapeKeyboardEvent);
|
314 | if (!this._ngxSmartModalService.getModalStack.length) {
|
315 | this._renderer.removeClass(document.body, 'dialog-open');
|
316 | }
|
317 | }
|
318 | |
319 |
|
320 |
|
321 |
|
322 | open(top) {
|
323 | if (top) {
|
324 | this.layerPosition = this._ngxSmartModalService.getHigherIndex();
|
325 | }
|
326 | this._renderer.addClass(document.body, 'dialog-open');
|
327 | this.overlayVisible = true;
|
328 | this.visible = true;
|
329 | setTimeout(() => {
|
330 | this.openedClass = true;
|
331 | if (this.target) {
|
332 | this.targetPlacement();
|
333 | }
|
334 | this._changeDetectorRef.markForCheck();
|
335 | });
|
336 | this.onOpen.emit(this);
|
337 | if (this.escapable) {
|
338 | window.addEventListener('keyup', this.escapeKeyboardEvent);
|
339 | }
|
340 | }
|
341 | |
342 |
|
343 |
|
344 | close() {
|
345 | const me = this;
|
346 | this.openedClass = false;
|
347 | this.onClose.emit(this);
|
348 | this.onAnyCloseEvent.emit(this);
|
349 | if (this._ngxSmartModalService.getOpenedModals().length < 2) {
|
350 | this._renderer.removeClass(document.body, 'dialog-open');
|
351 | }
|
352 | setTimeout(() => {
|
353 | me.visibleChange.emit(me.visible);
|
354 | me.visible = false;
|
355 | me.overlayVisible = false;
|
356 | me._changeDetectorRef.markForCheck();
|
357 | me.onCloseFinished.emit(me);
|
358 | me.onAnyCloseEventFinished.emit(me);
|
359 | }, this.hideDelay);
|
360 | window.removeEventListener('keyup', this.escapeKeyboardEvent);
|
361 | }
|
362 | |
363 |
|
364 |
|
365 |
|
366 | dismiss(e) {
|
367 | const me = this;
|
368 | if (!this.dismissable) {
|
369 | return;
|
370 | }
|
371 | if (e.target.classList.contains('overlay')) {
|
372 | this.openedClass = false;
|
373 | this.onDismiss.emit(this);
|
374 | this.onAnyCloseEvent.emit(this);
|
375 | if (this._ngxSmartModalService.getOpenedModals().length < 2) {
|
376 | this._renderer.removeClass(document.body, 'dialog-open');
|
377 | }
|
378 | setTimeout(() => {
|
379 | me.visible = false;
|
380 | me.visibleChange.emit(me.visible);
|
381 | me.overlayVisible = false;
|
382 | me._changeDetectorRef.markForCheck();
|
383 | me.onDismissFinished.emit(me);
|
384 | me.onAnyCloseEventFinished.emit(me);
|
385 | }, this.hideDelay);
|
386 | window.removeEventListener('keyup', this.escapeKeyboardEvent);
|
387 | }
|
388 | }
|
389 | |
390 |
|
391 |
|
392 |
|
393 | toggle(top) {
|
394 | if (this.visible) {
|
395 | this.close();
|
396 | }
|
397 | else {
|
398 | this.open(top);
|
399 | }
|
400 | }
|
401 | |
402 |
|
403 |
|
404 |
|
405 | addCustomClass(className) {
|
406 | if (!this.customClass.length) {
|
407 | this.customClass = className;
|
408 | }
|
409 | else {
|
410 | this.customClass += ' ' + className;
|
411 | }
|
412 | }
|
413 | |
414 |
|
415 |
|
416 |
|
417 | removeCustomClass(className) {
|
418 | if (className) {
|
419 | this.customClass = this.customClass.replace(className, '').trim();
|
420 | }
|
421 | else {
|
422 | this.customClass = '';
|
423 | }
|
424 | }
|
425 | |
426 |
|
427 |
|
428 | isVisible() {
|
429 | return this.visible;
|
430 | }
|
431 | |
432 |
|
433 |
|
434 | hasData() {
|
435 | return this._data !== undefined;
|
436 | }
|
437 | |
438 |
|
439 |
|
440 |
|
441 |
|
442 | setData(data, force) {
|
443 | if (!this.hasData() || (this.hasData() && force)) {
|
444 | this._data = data;
|
445 | this.onDataAdded.emit(this._data);
|
446 | this._changeDetectorRef.markForCheck();
|
447 | }
|
448 | }
|
449 | |
450 |
|
451 |
|
452 | getData() {
|
453 | return this._data;
|
454 | }
|
455 | |
456 |
|
457 |
|
458 | removeData() {
|
459 | this._data = undefined;
|
460 | this.onDataRemoved.emit(true);
|
461 | this._changeDetectorRef.markForCheck();
|
462 | }
|
463 | |
464 |
|
465 |
|
466 | targetPlacement() {
|
467 | if (!this.nsmDialog || !this.nsmContent || !this.nsmOverlay || !this.target) {
|
468 | return;
|
469 | }
|
470 | const targetElementRect = document.querySelector(this.target).getBoundingClientRect();
|
471 | const bodyRect = this.nsmOverlay.nativeElement.getBoundingClientRect();
|
472 | const nsmContentRect = this.nsmContent.nativeElement.getBoundingClientRect();
|
473 | const nsmDialogRect = this.nsmDialog.nativeElement.getBoundingClientRect();
|
474 | const marginLeft = parseInt( (getComputedStyle(this.nsmContent.nativeElement).marginLeft), 10);
|
475 | const marginTop = parseInt( (getComputedStyle(this.nsmContent.nativeElement).marginTop), 10);
|
476 | let offsetTop = targetElementRect.top - nsmDialogRect.top - ((nsmContentRect.height - targetElementRect.height) / 2);
|
477 | let offsetLeft = targetElementRect.left - nsmDialogRect.left - ((nsmContentRect.width - targetElementRect.width) / 2);
|
478 | if (offsetLeft + nsmDialogRect.left + nsmContentRect.width + (marginLeft * 2) > bodyRect.width) {
|
479 | offsetLeft = bodyRect.width - (nsmDialogRect.left + nsmContentRect.width) - (marginLeft * 2);
|
480 | }
|
481 | else if (offsetLeft + nsmDialogRect.left < 0) {
|
482 | offsetLeft = -nsmDialogRect.left;
|
483 | }
|
484 | if (offsetTop + nsmDialogRect.top + nsmContentRect.height + marginTop > bodyRect.height) {
|
485 | offsetTop = bodyRect.height - (nsmDialogRect.top + nsmContentRect.height) - marginTop;
|
486 | }
|
487 | if (offsetTop < 0) {
|
488 | offsetTop = 0;
|
489 | }
|
490 | this._renderer.setStyle(this.nsmContent.nativeElement, 'top', offsetTop + 'px');
|
491 | this._renderer.setStyle(this.nsmContent.nativeElement, 'left', offsetLeft + 'px');
|
492 | }
|
493 | }
|
494 | NgxSmartModalComponent.decorators = [
|
495 | { type: Component, args: [{
|
496 | selector: 'ngx-smart-modal',
|
497 | template: `
|
498 | <div *ngIf="overlayVisible"
|
499 | [style.z-index]="visible ? layerPosition-1 : -1"
|
500 | [ngClass]="{'transparent':!backdrop, 'overlay':true, 'nsm-overlay-open':openedClass}"
|
501 | (click)="dismiss($event)" #nsmOverlay>
|
502 | <div [style.z-index]="visible ? layerPosition : -1"
|
503 | [ngClass]="['nsm-dialog', customClass, openedClass ? 'nsm-dialog-open': 'nsm-dialog-close']" #nsmDialog>
|
504 | <div class="nsm-content" #nsmContent>
|
505 | <div class="nsm-body">
|
506 | <ng-content></ng-content>
|
507 | </div>
|
508 | <button type="button" *ngIf="closable" (click)="close()" aria-label="Close" class="nsm-dialog-btn-close">
|
509 | <img
|
510 | src="data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTkuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfMSIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCI+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTUwNS45NDMsNi4wNThjLTguMDc3LTguMDc3LTIxLjE3Mi04LjA3Ny0yOS4yNDksMEw2LjA1OCw0NzYuNjkzYy04LjA3Nyw4LjA3Ny04LjA3NywyMS4xNzIsMCwyOS4yNDkgICAgQzEwLjA5Niw1MDkuOTgyLDE1LjM5LDUxMiwyMC42ODMsNTEyYzUuMjkzLDAsMTAuNTg2LTIuMDE5LDE0LjYyNS02LjA1OUw1MDUuOTQzLDM1LjMwNiAgICBDNTE0LjAxOSwyNy4yMyw1MTQuMDE5LDE0LjEzNSw1MDUuOTQzLDYuMDU4eiIgZmlsbD0iIzAwMDAwMCIvPgoJPC9nPgo8L2c+CjxnPgoJPGc+CgkJPHBhdGggZD0iTTUwNS45NDIsNDc2LjY5NEwzNS4zMDYsNi4wNTljLTguMDc2LTguMDc3LTIxLjE3Mi04LjA3Ny0yOS4yNDgsMGMtOC4wNzcsOC4wNzYtOC4wNzcsMjEuMTcxLDAsMjkuMjQ4bDQ3MC42MzYsNDcwLjYzNiAgICBjNC4wMzgsNC4wMzksOS4zMzIsNi4wNTgsMTQuNjI1LDYuMDU4YzUuMjkzLDAsMTAuNTg3LTIuMDE5LDE0LjYyNC02LjA1N0M1MTQuMDE4LDQ5Ny44NjYsNTE0LjAxOCw0ODQuNzcxLDUwNS45NDIsNDc2LjY5NHoiIGZpbGw9IiMwMDAwMDAiLz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K" />
|
511 | </button>
|
512 | </div>
|
513 | </div>
|
514 | </div>
|
515 | `
|
516 | },] },
|
517 | ];
|
518 |
|
519 | NgxSmartModalComponent.ctorParameters = () => [
|
520 | { type: Renderer2, },
|
521 | { type: ChangeDetectorRef, },
|
522 | { type: NgxSmartModalService, },
|
523 | ];
|
524 | NgxSmartModalComponent.propDecorators = {
|
525 | "closable": [{ type: Input },],
|
526 | "escapable": [{ type: Input },],
|
527 | "dismissable": [{ type: Input },],
|
528 | "identifier": [{ type: Input },],
|
529 | "customClass": [{ type: Input },],
|
530 | "visible": [{ type: Input },],
|
531 | "backdrop": [{ type: Input },],
|
532 | "force": [{ type: Input },],
|
533 | "hideDelay": [{ type: Input },],
|
534 | "autostart": [{ type: Input },],
|
535 | "target": [{ type: Input },],
|
536 | "visibleChange": [{ type: Output },],
|
537 | "onClose": [{ type: Output },],
|
538 | "onCloseFinished": [{ type: Output },],
|
539 | "onDismiss": [{ type: Output },],
|
540 | "onDismissFinished": [{ type: Output },],
|
541 | "onAnyCloseEvent": [{ type: Output },],
|
542 | "onAnyCloseEventFinished": [{ type: Output },],
|
543 | "onOpen": [{ type: Output },],
|
544 | "onEscape": [{ type: Output },],
|
545 | "onDataAdded": [{ type: Output },],
|
546 | "onDataRemoved": [{ type: Output },],
|
547 | "nsmContent": [{ type: ViewChild, args: ['nsmContent',] },],
|
548 | "nsmDialog": [{ type: ViewChild, args: ['nsmDialog',] },],
|
549 | "nsmOverlay": [{ type: ViewChild, args: ['nsmOverlay',] },],
|
550 | "targetPlacement": [{ type: HostListener, args: ['window:resize',] },],
|
551 | };
|
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 | class NgxSmartModalModule {
|
558 | |
559 |
|
560 |
|
561 |
|
562 | static forRoot() {
|
563 | return {
|
564 | ngModule: NgxSmartModalModule,
|
565 | providers: [NgxSmartModalService]
|
566 | };
|
567 | }
|
568 | |
569 |
|
570 |
|
571 |
|
572 | static forChild() {
|
573 | return {
|
574 | ngModule: NgxSmartModalModule,
|
575 | providers: [NgxSmartModalService]
|
576 | };
|
577 | }
|
578 | }
|
579 | NgxSmartModalModule.decorators = [
|
580 | { type: NgModule, args: [{
|
581 | declarations: [NgxSmartModalComponent],
|
582 | exports: [NgxSmartModalComponent],
|
583 | imports: [CommonModule]
|
584 | },] },
|
585 | ];
|
586 |
|
587 | NgxSmartModalModule.ctorParameters = () => [];
|
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 |
|
601 |
|
602 |
|
603 |
|
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 |
|
611 | export { NgxSmartModalService, NgxSmartModalComponent, NgxSmartModalModule };
|
612 |
|