ngx-bootstrap-modal.js 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036
  1. import { Injectable, Component, ElementRef, Renderer2, HostListener, EventEmitter, Directive, ViewContainerRef, Input, Output, RendererFactory2, NgModule } from '@angular/core';
  2. import { isBs3, Utils, document as document$1, window as window$1 } from 'ngx-bootstrap/utils';
  3. import { ComponentLoaderFactory } from 'ngx-bootstrap/component-loader';
  4. import { PositioningService } from 'ngx-bootstrap/positioning';
  5. /**
  6. * @fileoverview added by tsickle
  7. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  8. */
  9. class BsModalRef {
  10. constructor() {
  11. /**
  12. * Hides the modal
  13. */
  14. this.hide = Function;
  15. /**
  16. * Sets new class to modal window
  17. */
  18. this.setClass = Function;
  19. }
  20. }
  21. BsModalRef.decorators = [
  22. { type: Injectable }
  23. ];
  24. /**
  25. * @fileoverview added by tsickle
  26. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  27. */
  28. class ModalBackdropOptions {
  29. /**
  30. * @param {?} options
  31. */
  32. constructor(options) {
  33. this.animate = true;
  34. Object.assign(this, options);
  35. }
  36. }
  37. /**
  38. * @fileoverview added by tsickle
  39. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  40. */
  41. class ModalOptions {
  42. }
  43. ModalOptions.decorators = [
  44. { type: Injectable }
  45. ];
  46. /** @type {?} */
  47. const modalConfigDefaults = {
  48. backdrop: true,
  49. keyboard: true,
  50. focus: true,
  51. show: false,
  52. ignoreBackdropClick: false,
  53. class: '',
  54. animated: true,
  55. initialState: {}
  56. };
  57. /** @type {?} */
  58. const CLASS_NAME = {
  59. SCROLLBAR_MEASURER: 'modal-scrollbar-measure',
  60. BACKDROP: 'modal-backdrop',
  61. OPEN: 'modal-open',
  62. FADE: 'fade',
  63. IN: 'in',
  64. // bs3
  65. SHOW: 'show' // bs4
  66. };
  67. /** @type {?} */
  68. const TRANSITION_DURATIONS = {
  69. MODAL: 300,
  70. BACKDROP: 150
  71. };
  72. /** @type {?} */
  73. const DISMISS_REASONS = {
  74. BACKRDOP: 'backdrop-click',
  75. ESC: 'esc'
  76. };
  77. /**
  78. * @fileoverview added by tsickle
  79. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  80. */
  81. class ModalContainerComponent {
  82. /**
  83. * @param {?} options
  84. * @param {?} _element
  85. * @param {?} _renderer
  86. */
  87. constructor(options, _element, _renderer) {
  88. this._element = _element;
  89. this._renderer = _renderer;
  90. this.isShown = false;
  91. this.isModalHiding = false;
  92. this.config = Object.assign({}, options);
  93. }
  94. /**
  95. * @return {?}
  96. */
  97. ngOnInit() {
  98. if (this.isAnimated) {
  99. this._renderer.addClass(this._element.nativeElement, CLASS_NAME.FADE);
  100. }
  101. this._renderer.setStyle(this._element.nativeElement, 'display', 'block');
  102. setTimeout((/**
  103. * @return {?}
  104. */
  105. () => {
  106. this.isShown = true;
  107. this._renderer.addClass(this._element.nativeElement, isBs3() ? CLASS_NAME.IN : CLASS_NAME.SHOW);
  108. }), this.isAnimated ? TRANSITION_DURATIONS.BACKDROP : 0);
  109. if (document && document.body) {
  110. if (this.bsModalService.getModalsCount() === 1) {
  111. this.bsModalService.checkScrollbar();
  112. this.bsModalService.setScrollbar();
  113. }
  114. this._renderer.addClass(document.body, CLASS_NAME.OPEN);
  115. }
  116. if (this._element.nativeElement) {
  117. this._element.nativeElement.focus();
  118. }
  119. }
  120. /**
  121. * @param {?} event
  122. * @return {?}
  123. */
  124. onClick(event) {
  125. if (this.config.ignoreBackdropClick ||
  126. this.config.backdrop === 'static' ||
  127. event.target !== this._element.nativeElement) {
  128. return;
  129. }
  130. this.bsModalService.setDismissReason(DISMISS_REASONS.BACKRDOP);
  131. this.hide();
  132. }
  133. /**
  134. * @param {?} event
  135. * @return {?}
  136. */
  137. onEsc(event) {
  138. if (!this.isShown) {
  139. return;
  140. }
  141. // tslint:disable-next-line:deprecation
  142. if (event.keyCode === 27 || event.key === 'Escape') {
  143. event.preventDefault();
  144. }
  145. if (this.config.keyboard &&
  146. this.level === this.bsModalService.getModalsCount()) {
  147. this.bsModalService.setDismissReason(DISMISS_REASONS.ESC);
  148. this.hide();
  149. }
  150. }
  151. /**
  152. * @return {?}
  153. */
  154. ngOnDestroy() {
  155. if (this.isShown) {
  156. this.hide();
  157. }
  158. }
  159. /**
  160. * @return {?}
  161. */
  162. hide() {
  163. if (this.isModalHiding || !this.isShown) {
  164. return;
  165. }
  166. this.isModalHiding = true;
  167. this._renderer.removeClass(this._element.nativeElement, isBs3() ? CLASS_NAME.IN : CLASS_NAME.SHOW);
  168. setTimeout((/**
  169. * @return {?}
  170. */
  171. () => {
  172. this.isShown = false;
  173. if (document &&
  174. document.body &&
  175. this.bsModalService.getModalsCount() === 1) {
  176. this._renderer.removeClass(document.body, CLASS_NAME.OPEN);
  177. }
  178. this.bsModalService.hide(this.level);
  179. this.isModalHiding = false;
  180. }), this.isAnimated ? TRANSITION_DURATIONS.MODAL : 0);
  181. }
  182. }
  183. ModalContainerComponent.decorators = [
  184. { type: Component, args: [{
  185. selector: 'modal-container',
  186. template: `
  187. <div [class]="'modal-dialog' + (config.class ? ' ' + config.class : '')" role="document">
  188. <div class="modal-content">
  189. <ng-content></ng-content>
  190. </div>
  191. </div>
  192. `,
  193. host: {
  194. class: 'modal',
  195. role: 'dialog',
  196. tabindex: '-1',
  197. '[attr.aria-modal]': 'true'
  198. }
  199. }] }
  200. ];
  201. /** @nocollapse */
  202. ModalContainerComponent.ctorParameters = () => [
  203. { type: ModalOptions },
  204. { type: ElementRef },
  205. { type: Renderer2 }
  206. ];
  207. ModalContainerComponent.propDecorators = {
  208. onClick: [{ type: HostListener, args: ['click', ['$event'],] }],
  209. onEsc: [{ type: HostListener, args: ['window:keydown.esc', ['$event'],] }]
  210. };
  211. /**
  212. * @fileoverview added by tsickle
  213. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  214. */
  215. /**
  216. * This component will be added as background layout for modals if enabled
  217. */
  218. class ModalBackdropComponent {
  219. /**
  220. * @param {?} element
  221. * @param {?} renderer
  222. */
  223. constructor(element, renderer) {
  224. this._isShown = false;
  225. this.element = element;
  226. this.renderer = renderer;
  227. }
  228. /**
  229. * @return {?}
  230. */
  231. get isAnimated() {
  232. return this._isAnimated;
  233. }
  234. /**
  235. * @param {?} value
  236. * @return {?}
  237. */
  238. set isAnimated(value) {
  239. this._isAnimated = value;
  240. // this.renderer.setElementClass(this.element.nativeElement, `${ClassName.FADE}`, value);
  241. }
  242. /**
  243. * @return {?}
  244. */
  245. get isShown() {
  246. return this._isShown;
  247. }
  248. /**
  249. * @param {?} value
  250. * @return {?}
  251. */
  252. set isShown(value) {
  253. this._isShown = value;
  254. if (value) {
  255. this.renderer.addClass(this.element.nativeElement, `${CLASS_NAME.IN}`);
  256. }
  257. else {
  258. this.renderer.removeClass(this.element.nativeElement, `${CLASS_NAME.IN}`);
  259. }
  260. if (!isBs3()) {
  261. if (value) {
  262. this.renderer.addClass(this.element.nativeElement, `${CLASS_NAME.SHOW}`);
  263. }
  264. else {
  265. this.renderer.removeClass(this.element.nativeElement, `${CLASS_NAME.SHOW}`);
  266. }
  267. }
  268. }
  269. /**
  270. * @return {?}
  271. */
  272. ngOnInit() {
  273. if (this.isAnimated) {
  274. this.renderer.addClass(this.element.nativeElement, `${CLASS_NAME.FADE}`);
  275. Utils.reflow(this.element.nativeElement);
  276. }
  277. this.isShown = true;
  278. }
  279. }
  280. ModalBackdropComponent.decorators = [
  281. { type: Component, args: [{
  282. selector: 'bs-modal-backdrop',
  283. template: ' ',
  284. host: { class: CLASS_NAME.BACKDROP }
  285. }] }
  286. ];
  287. /** @nocollapse */
  288. ModalBackdropComponent.ctorParameters = () => [
  289. { type: ElementRef },
  290. { type: Renderer2 }
  291. ];
  292. /**
  293. * @fileoverview added by tsickle
  294. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  295. */
  296. /** @type {?} */
  297. const TRANSITION_DURATION = 300;
  298. /** @type {?} */
  299. const BACKDROP_TRANSITION_DURATION = 150;
  300. /**
  301. * Mark any code with directive to show it's content in modal
  302. */
  303. class ModalDirective {
  304. /**
  305. * @param {?} _element
  306. * @param {?} _viewContainerRef
  307. * @param {?} _renderer
  308. * @param {?} clf
  309. */
  310. constructor(_element, _viewContainerRef, _renderer, clf) {
  311. this._element = _element;
  312. this._renderer = _renderer;
  313. /**
  314. * This event fires immediately when the `show` instance method is called.
  315. */
  316. this.onShow = new EventEmitter();
  317. /**
  318. * This event is fired when the modal has been made visible to the user
  319. * (will wait for CSS transitions to complete)
  320. */
  321. this.onShown = new EventEmitter();
  322. /**
  323. * This event is fired immediately when
  324. * the hide instance method has been called.
  325. */
  326. this.onHide = new EventEmitter();
  327. /**
  328. * This event is fired when the modal has finished being
  329. * hidden from the user (will wait for CSS transitions to complete).
  330. */
  331. this.onHidden = new EventEmitter();
  332. this._isShown = false;
  333. this.isBodyOverflowing = false;
  334. this.originalBodyPadding = 0;
  335. this.scrollbarWidth = 0;
  336. this.timerHideModal = 0;
  337. this.timerRmBackDrop = 0;
  338. this.isNested = false;
  339. this._backdrop = clf.createLoader(_element, _viewContainerRef, _renderer);
  340. }
  341. /**
  342. * allows to set modal configuration via element property
  343. * @param {?} conf
  344. * @return {?}
  345. */
  346. set config(conf) {
  347. this._config = this.getConfig(conf);
  348. }
  349. /**
  350. * @return {?}
  351. */
  352. get config() {
  353. return this._config;
  354. }
  355. /**
  356. * @return {?}
  357. */
  358. get isShown() {
  359. return this._isShown;
  360. }
  361. /**
  362. * @param {?} event
  363. * @return {?}
  364. */
  365. onClick(event) {
  366. if (this.config.ignoreBackdropClick ||
  367. this.config.backdrop === 'static' ||
  368. event.target !== this._element.nativeElement) {
  369. return;
  370. }
  371. this.dismissReason = DISMISS_REASONS.BACKRDOP;
  372. this.hide(event);
  373. }
  374. // todo: consider preventing default and stopping propagation
  375. /**
  376. * @param {?} event
  377. * @return {?}
  378. */
  379. onEsc(event) {
  380. if (!this._isShown) {
  381. return;
  382. }
  383. // tslint:disable-next-line:deprecation
  384. if (event.keyCode === 27 || event.key === 'Escape') {
  385. event.preventDefault();
  386. }
  387. if (this.config.keyboard) {
  388. this.dismissReason = DISMISS_REASONS.ESC;
  389. this.hide();
  390. }
  391. }
  392. /**
  393. * @return {?}
  394. */
  395. ngOnDestroy() {
  396. this.config = void 0;
  397. if (this._isShown) {
  398. this._isShown = false;
  399. this.hideModal();
  400. this._backdrop.dispose();
  401. }
  402. }
  403. /**
  404. * @return {?}
  405. */
  406. ngOnInit() {
  407. this._config = this._config || this.getConfig();
  408. setTimeout((/**
  409. * @return {?}
  410. */
  411. () => {
  412. if (this._config.show) {
  413. this.show();
  414. }
  415. }), 0);
  416. }
  417. /* Public methods */
  418. /**
  419. * Allows to manually toggle modal visibility
  420. * @return {?}
  421. */
  422. toggle() {
  423. return this._isShown ? this.hide() : this.show();
  424. }
  425. /**
  426. * Allows to manually open modal
  427. * @return {?}
  428. */
  429. show() {
  430. this.dismissReason = null;
  431. this.onShow.emit(this);
  432. if (this._isShown) {
  433. return;
  434. }
  435. clearTimeout(this.timerHideModal);
  436. clearTimeout(this.timerRmBackDrop);
  437. this._isShown = true;
  438. this.checkScrollbar();
  439. this.setScrollbar();
  440. if (document$1 && document$1.body) {
  441. if (document$1.body.classList.contains(CLASS_NAME.OPEN)) {
  442. this.isNested = true;
  443. }
  444. else {
  445. this._renderer.addClass(document$1.body, CLASS_NAME.OPEN);
  446. }
  447. }
  448. this.showBackdrop((/**
  449. * @return {?}
  450. */
  451. () => {
  452. this.showElement();
  453. }));
  454. }
  455. /**
  456. * Allows to manually close modal
  457. * @param {?=} event
  458. * @return {?}
  459. */
  460. hide(event) {
  461. if (event) {
  462. event.preventDefault();
  463. }
  464. this.onHide.emit(this);
  465. // todo: add an option to prevent hiding
  466. if (!this._isShown) {
  467. return;
  468. }
  469. window$1.clearTimeout(this.timerHideModal);
  470. window$1.clearTimeout(this.timerRmBackDrop);
  471. this._isShown = false;
  472. this._renderer.removeClass(this._element.nativeElement, CLASS_NAME.IN);
  473. if (!isBs3()) {
  474. this._renderer.removeClass(this._element.nativeElement, CLASS_NAME.SHOW);
  475. }
  476. // this._addClassIn = false;
  477. if (this._config.animated) {
  478. this.timerHideModal = window$1.setTimeout((/**
  479. * @return {?}
  480. */
  481. () => this.hideModal()), TRANSITION_DURATION);
  482. }
  483. else {
  484. this.hideModal();
  485. }
  486. }
  487. /**
  488. * Private methods \@internal
  489. * @protected
  490. * @param {?=} config
  491. * @return {?}
  492. */
  493. getConfig(config) {
  494. return Object.assign({}, modalConfigDefaults, config);
  495. }
  496. /**
  497. * Show dialog
  498. * \@internal
  499. * @protected
  500. * @return {?}
  501. */
  502. showElement() {
  503. // todo: replace this with component loader usage
  504. if (!this._element.nativeElement.parentNode ||
  505. this._element.nativeElement.parentNode.nodeType !== Node.ELEMENT_NODE) {
  506. // don't move modals dom position
  507. if (document$1 && document$1.body) {
  508. document$1.body.appendChild(this._element.nativeElement);
  509. }
  510. }
  511. this._renderer.setAttribute(this._element.nativeElement, 'aria-hidden', 'false');
  512. this._renderer.setAttribute(this._element.nativeElement, 'aria-modal', 'true');
  513. this._renderer.setStyle(this._element.nativeElement, 'display', 'block');
  514. this._renderer.setProperty(this._element.nativeElement, 'scrollTop', 0);
  515. if (this._config.animated) {
  516. Utils.reflow(this._element.nativeElement);
  517. }
  518. // this._addClassIn = true;
  519. this._renderer.addClass(this._element.nativeElement, CLASS_NAME.IN);
  520. if (!isBs3()) {
  521. this._renderer.addClass(this._element.nativeElement, CLASS_NAME.SHOW);
  522. }
  523. /** @type {?} */
  524. const transitionComplete = (/**
  525. * @return {?}
  526. */
  527. () => {
  528. if (this._config.focus) {
  529. this._element.nativeElement.focus();
  530. }
  531. this.onShown.emit(this);
  532. });
  533. if (this._config.animated) {
  534. setTimeout(transitionComplete, TRANSITION_DURATION);
  535. }
  536. else {
  537. transitionComplete();
  538. }
  539. }
  540. /**
  541. * \@internal
  542. * @protected
  543. * @return {?}
  544. */
  545. hideModal() {
  546. this._renderer.setAttribute(this._element.nativeElement, 'aria-hidden', 'true');
  547. this._renderer.setStyle(this._element.nativeElement, 'display', 'none');
  548. this.showBackdrop((/**
  549. * @return {?}
  550. */
  551. () => {
  552. if (!this.isNested) {
  553. if (document$1 && document$1.body) {
  554. this._renderer.removeClass(document$1.body, CLASS_NAME.OPEN);
  555. }
  556. this.resetScrollbar();
  557. }
  558. this.resetAdjustments();
  559. this.focusOtherModal();
  560. this.onHidden.emit(this);
  561. }));
  562. }
  563. // todo: original show was calling a callback when done, but we can use
  564. // promise
  565. /**
  566. * \@internal
  567. * @protected
  568. * @param {?=} callback
  569. * @return {?}
  570. */
  571. showBackdrop(callback) {
  572. if (this._isShown &&
  573. this.config.backdrop &&
  574. (!this.backdrop || !this.backdrop.instance.isShown)) {
  575. this.removeBackdrop();
  576. this._backdrop
  577. .attach(ModalBackdropComponent)
  578. .to('body')
  579. .show({ isAnimated: this._config.animated });
  580. this.backdrop = this._backdrop._componentRef;
  581. if (!callback) {
  582. return;
  583. }
  584. if (!this._config.animated) {
  585. callback();
  586. return;
  587. }
  588. setTimeout(callback, BACKDROP_TRANSITION_DURATION);
  589. }
  590. else if (!this._isShown && this.backdrop) {
  591. this.backdrop.instance.isShown = false;
  592. /** @type {?} */
  593. const callbackRemove = (/**
  594. * @return {?}
  595. */
  596. () => {
  597. this.removeBackdrop();
  598. if (callback) {
  599. callback();
  600. }
  601. });
  602. if (this.backdrop.instance.isAnimated) {
  603. this.timerRmBackDrop = window$1.setTimeout(callbackRemove, BACKDROP_TRANSITION_DURATION);
  604. }
  605. else {
  606. callbackRemove();
  607. }
  608. }
  609. else if (callback) {
  610. callback();
  611. }
  612. }
  613. /**
  614. * \@internal
  615. * @protected
  616. * @return {?}
  617. */
  618. removeBackdrop() {
  619. this._backdrop.hide();
  620. }
  621. /**
  622. * Events tricks
  623. * @protected
  624. * @return {?}
  625. */
  626. // no need for it
  627. // protected setEscapeEvent():void {
  628. // if (this._isShown && this._config.keyboard) {
  629. // $(this._element).on(Event.KEYDOWN_DISMISS, (event) => {
  630. // if (event.which === 27) {
  631. // this.hide()
  632. // }
  633. // })
  634. //
  635. // } else if (!this._isShown) {
  636. // $(this._element).off(Event.KEYDOWN_DISMISS)
  637. // }
  638. // }
  639. // protected setResizeEvent():void {
  640. // console.log(this.renderer.listenGlobal('', Event.RESIZE));
  641. // if (this._isShown) {
  642. // $(window).on(Event.RESIZE, $.proxy(this._handleUpdate, this))
  643. // } else {
  644. // $(window).off(Event.RESIZE)
  645. // }
  646. // }
  647. focusOtherModal() {
  648. if (this._element.nativeElement.parentElement == null) {
  649. return;
  650. }
  651. /** @type {?} */
  652. const otherOpenedModals = this._element.nativeElement.parentElement.querySelectorAll('.in[bsModal]');
  653. if (!otherOpenedModals.length) {
  654. return;
  655. }
  656. otherOpenedModals[otherOpenedModals.length - 1].focus();
  657. }
  658. /**
  659. * \@internal
  660. * @protected
  661. * @return {?}
  662. */
  663. resetAdjustments() {
  664. this._renderer.setStyle(this._element.nativeElement, 'paddingLeft', '');
  665. this._renderer.setStyle(this._element.nativeElement, 'paddingRight', '');
  666. }
  667. /** Scroll bar tricks */
  668. /**
  669. * \@internal
  670. * @protected
  671. * @return {?}
  672. */
  673. checkScrollbar() {
  674. this.isBodyOverflowing = document$1.body.clientWidth < window$1.innerWidth;
  675. this.scrollbarWidth = this.getScrollbarWidth();
  676. }
  677. /**
  678. * @protected
  679. * @return {?}
  680. */
  681. setScrollbar() {
  682. if (!document$1) {
  683. return;
  684. }
  685. this.originalBodyPadding = parseInt(window$1
  686. .getComputedStyle(document$1.body)
  687. .getPropertyValue('padding-right') || 0, 10);
  688. if (this.isBodyOverflowing) {
  689. document$1.body.style.paddingRight = `${this.originalBodyPadding +
  690. this.scrollbarWidth}px`;
  691. }
  692. }
  693. /**
  694. * @protected
  695. * @return {?}
  696. */
  697. resetScrollbar() {
  698. document$1.body.style.paddingRight = `${this.originalBodyPadding}px`;
  699. }
  700. // thx d.walsh
  701. /**
  702. * @protected
  703. * @return {?}
  704. */
  705. getScrollbarWidth() {
  706. /** @type {?} */
  707. const scrollDiv = this._renderer.createElement('div');
  708. this._renderer.addClass(scrollDiv, CLASS_NAME.SCROLLBAR_MEASURER);
  709. this._renderer.appendChild(document$1.body, scrollDiv);
  710. /** @type {?} */
  711. const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
  712. this._renderer.removeChild(document$1.body, scrollDiv);
  713. return scrollbarWidth;
  714. }
  715. }
  716. ModalDirective.decorators = [
  717. { type: Directive, args: [{
  718. selector: '[bsModal]',
  719. exportAs: 'bs-modal'
  720. },] }
  721. ];
  722. /** @nocollapse */
  723. ModalDirective.ctorParameters = () => [
  724. { type: ElementRef },
  725. { type: ViewContainerRef },
  726. { type: Renderer2 },
  727. { type: ComponentLoaderFactory }
  728. ];
  729. ModalDirective.propDecorators = {
  730. config: [{ type: Input }],
  731. onShow: [{ type: Output }],
  732. onShown: [{ type: Output }],
  733. onHide: [{ type: Output }],
  734. onHidden: [{ type: Output }],
  735. onClick: [{ type: HostListener, args: ['click', ['$event'],] }],
  736. onEsc: [{ type: HostListener, args: ['keydown.esc', ['$event'],] }]
  737. };
  738. /**
  739. * @fileoverview added by tsickle
  740. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  741. */
  742. class BsModalService {
  743. /**
  744. * @param {?} rendererFactory
  745. * @param {?} clf
  746. */
  747. constructor(rendererFactory, clf) {
  748. this.clf = clf;
  749. // constructor props
  750. this.config = modalConfigDefaults;
  751. // tslint:disable-next-line:no-any
  752. this.onShow = new EventEmitter();
  753. // tslint:disable-next-line:no-any
  754. this.onShown = new EventEmitter();
  755. // tslint:disable-next-line:no-any
  756. this.onHide = new EventEmitter();
  757. // tslint:disable-next-line:no-any
  758. this.onHidden = new EventEmitter();
  759. this.isBodyOverflowing = false;
  760. this.originalBodyPadding = 0;
  761. this.scrollbarWidth = 0;
  762. this.modalsCount = 0;
  763. this.lastDismissReason = '';
  764. this.loaders = [];
  765. this._backdropLoader = this.clf.createLoader(null, null, null);
  766. this._renderer = rendererFactory.createRenderer(null, null);
  767. }
  768. /**
  769. * Shows a modal
  770. * @param {?} content
  771. * @param {?=} config
  772. * @return {?}
  773. */
  774. // tslint:disable-next-line:no-any
  775. show(content, config) {
  776. this.modalsCount++;
  777. this._createLoaders();
  778. this.config = Object.assign({}, modalConfigDefaults, config);
  779. this._showBackdrop();
  780. this.lastDismissReason = null;
  781. return this._showModal(content);
  782. }
  783. /**
  784. * @param {?} level
  785. * @return {?}
  786. */
  787. hide(level) {
  788. if (this.modalsCount === 1) {
  789. this._hideBackdrop();
  790. this.resetScrollbar();
  791. }
  792. this.modalsCount = this.modalsCount >= 1 ? this.modalsCount - 1 : 0;
  793. setTimeout((/**
  794. * @return {?}
  795. */
  796. () => {
  797. this._hideModal(level);
  798. this.removeLoaders(level);
  799. }), this.config.animated ? TRANSITION_DURATIONS.BACKDROP : 0);
  800. }
  801. /**
  802. * @return {?}
  803. */
  804. _showBackdrop() {
  805. /** @type {?} */
  806. const isBackdropEnabled = this.config.backdrop || this.config.backdrop === 'static';
  807. /** @type {?} */
  808. const isBackdropInDOM = !this.backdropRef || !this.backdropRef.instance.isShown;
  809. if (this.modalsCount === 1) {
  810. this.removeBackdrop();
  811. if (isBackdropEnabled && isBackdropInDOM) {
  812. this._backdropLoader
  813. .attach(ModalBackdropComponent)
  814. .to('body')
  815. .show({ isAnimated: this.config.animated });
  816. this.backdropRef = this._backdropLoader._componentRef;
  817. }
  818. }
  819. }
  820. /**
  821. * @return {?}
  822. */
  823. _hideBackdrop() {
  824. if (!this.backdropRef) {
  825. return;
  826. }
  827. this.backdropRef.instance.isShown = false;
  828. /** @type {?} */
  829. const duration = this.config.animated ? TRANSITION_DURATIONS.BACKDROP : 0;
  830. setTimeout((/**
  831. * @return {?}
  832. */
  833. () => this.removeBackdrop()), duration);
  834. }
  835. // tslint:disable-next-line:no-any
  836. /**
  837. * @param {?} content
  838. * @return {?}
  839. */
  840. _showModal(content) {
  841. /** @type {?} */
  842. const modalLoader = this.loaders[this.loaders.length - 1];
  843. /** @type {?} */
  844. const bsModalRef = new BsModalRef();
  845. /** @type {?} */
  846. const modalContainerRef = modalLoader
  847. .provide({ provide: ModalOptions, useValue: this.config })
  848. .provide({ provide: BsModalRef, useValue: bsModalRef })
  849. .attach(ModalContainerComponent)
  850. .to('body')
  851. .show({ content, isAnimated: this.config.animated, initialState: this.config.initialState, bsModalService: this });
  852. modalContainerRef.instance.level = this.getModalsCount();
  853. bsModalRef.hide = (/**
  854. * @return {?}
  855. */
  856. () => {
  857. modalContainerRef.instance.hide();
  858. });
  859. bsModalRef.content = modalLoader.getInnerComponent() || null;
  860. bsModalRef.setClass = (/**
  861. * @param {?} newClass
  862. * @return {?}
  863. */
  864. (newClass) => {
  865. modalContainerRef.instance.config.class = newClass;
  866. });
  867. return bsModalRef;
  868. }
  869. /**
  870. * @param {?} level
  871. * @return {?}
  872. */
  873. _hideModal(level) {
  874. /** @type {?} */
  875. const modalLoader = this.loaders[level - 1];
  876. if (modalLoader) {
  877. modalLoader.hide();
  878. }
  879. }
  880. /**
  881. * @return {?}
  882. */
  883. getModalsCount() {
  884. return this.modalsCount;
  885. }
  886. /**
  887. * @param {?} reason
  888. * @return {?}
  889. */
  890. setDismissReason(reason) {
  891. this.lastDismissReason = reason;
  892. }
  893. /**
  894. * @return {?}
  895. */
  896. removeBackdrop() {
  897. this._backdropLoader.hide();
  898. this.backdropRef = null;
  899. }
  900. /** AFTER PR MERGE MODAL.COMPONENT WILL BE USING THIS CODE */
  901. /** Scroll bar tricks */
  902. /**
  903. * \@internal
  904. * @return {?}
  905. */
  906. checkScrollbar() {
  907. this.isBodyOverflowing = document.body.clientWidth < window.innerWidth;
  908. this.scrollbarWidth = this.getScrollbarWidth();
  909. }
  910. /**
  911. * @return {?}
  912. */
  913. setScrollbar() {
  914. if (!document) {
  915. return;
  916. }
  917. this.originalBodyPadding = parseInt(window
  918. .getComputedStyle(document.body)
  919. .getPropertyValue('padding-right') || '0', 10);
  920. if (this.isBodyOverflowing) {
  921. document.body.style.paddingRight = `${this.originalBodyPadding +
  922. this.scrollbarWidth}px`;
  923. }
  924. }
  925. /**
  926. * @private
  927. * @return {?}
  928. */
  929. resetScrollbar() {
  930. document.body.style.paddingRight = `${this.originalBodyPadding}px`;
  931. }
  932. // thx d.walsh
  933. /**
  934. * @private
  935. * @return {?}
  936. */
  937. getScrollbarWidth() {
  938. /** @type {?} */
  939. const scrollDiv = this._renderer.createElement('div');
  940. this._renderer.addClass(scrollDiv, CLASS_NAME.SCROLLBAR_MEASURER);
  941. this._renderer.appendChild(document.body, scrollDiv);
  942. /** @type {?} */
  943. const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
  944. this._renderer.removeChild(document.body, scrollDiv);
  945. return scrollbarWidth;
  946. }
  947. /**
  948. * @private
  949. * @return {?}
  950. */
  951. _createLoaders() {
  952. /** @type {?} */
  953. const loader = this.clf.createLoader(null, null, null);
  954. this.copyEvent(loader.onBeforeShow, this.onShow);
  955. this.copyEvent(loader.onShown, this.onShown);
  956. this.copyEvent(loader.onBeforeHide, this.onHide);
  957. this.copyEvent(loader.onHidden, this.onHidden);
  958. this.loaders.push(loader);
  959. }
  960. /**
  961. * @private
  962. * @param {?} level
  963. * @return {?}
  964. */
  965. removeLoaders(level) {
  966. this.loaders.splice(level - 1, 1);
  967. this.loaders.forEach((/**
  968. * @param {?} loader
  969. * @param {?} i
  970. * @return {?}
  971. */
  972. (loader, i) => {
  973. loader.instance.level = i + 1;
  974. }));
  975. }
  976. // tslint:disable-next-line:no-any
  977. /**
  978. * @private
  979. * @param {?} from
  980. * @param {?} to
  981. * @return {?}
  982. */
  983. copyEvent(from, to) {
  984. from.subscribe((/**
  985. * @return {?}
  986. */
  987. () => {
  988. to.emit(this.lastDismissReason);
  989. }));
  990. }
  991. }
  992. BsModalService.decorators = [
  993. { type: Injectable }
  994. ];
  995. /** @nocollapse */
  996. BsModalService.ctorParameters = () => [
  997. { type: RendererFactory2 },
  998. { type: ComponentLoaderFactory }
  999. ];
  1000. /**
  1001. * @fileoverview added by tsickle
  1002. * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
  1003. */
  1004. class ModalModule {
  1005. /**
  1006. * @return {?}
  1007. */
  1008. static forRoot() {
  1009. return {
  1010. ngModule: ModalModule,
  1011. providers: [BsModalService, ComponentLoaderFactory, PositioningService]
  1012. };
  1013. }
  1014. }
  1015. ModalModule.decorators = [
  1016. { type: NgModule, args: [{
  1017. declarations: [
  1018. ModalBackdropComponent,
  1019. ModalDirective,
  1020. ModalContainerComponent
  1021. ],
  1022. exports: [ModalBackdropComponent, ModalDirective],
  1023. entryComponents: [ModalBackdropComponent, ModalContainerComponent]
  1024. },] }
  1025. ];
  1026. export { BsModalRef, BsModalService, ModalBackdropComponent, ModalBackdropOptions, ModalContainerComponent, ModalDirective, ModalModule, ModalOptions, CLASS_NAME as ɵa };
  1027. //# sourceMappingURL=ngx-bootstrap-modal.js.map