You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1042 lines
30 KiB

  1. /*!
  2. * Bootstrap modal.js v5.1.3 (https://getbootstrap.com/)
  3. * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
  8. typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
  10. })(this, (function (EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
  11. const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
  12. const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
  13. const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
  14. const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
  15. const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
  16. /**
  17. * --------------------------------------------------------------------------
  18. * Bootstrap (v5.1.3): util/index.js
  19. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  20. * --------------------------------------------------------------------------
  21. */
  22. const MILLISECONDS_MULTIPLIER = 1000;
  23. const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
  24. const toType = obj => {
  25. if (obj === null || obj === undefined) {
  26. return `${obj}`;
  27. }
  28. return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
  29. };
  30. const getSelector = element => {
  31. let selector = element.getAttribute('data-bs-target');
  32. if (!selector || selector === '#') {
  33. let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
  34. // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
  35. // `document.querySelector` will rightfully complain it is invalid.
  36. // See https://github.com/twbs/bootstrap/issues/32273
  37. if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
  38. return null;
  39. } // Just in case some CMS puts out a full URL with the anchor appended
  40. if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
  41. hrefAttr = `#${hrefAttr.split('#')[1]}`;
  42. }
  43. selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
  44. }
  45. return selector;
  46. };
  47. const getElementFromSelector = element => {
  48. const selector = getSelector(element);
  49. return selector ? document.querySelector(selector) : null;
  50. };
  51. const getTransitionDurationFromElement = element => {
  52. if (!element) {
  53. return 0;
  54. } // Get transition-duration of the element
  55. let {
  56. transitionDuration,
  57. transitionDelay
  58. } = window.getComputedStyle(element);
  59. const floatTransitionDuration = Number.parseFloat(transitionDuration);
  60. const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
  61. if (!floatTransitionDuration && !floatTransitionDelay) {
  62. return 0;
  63. } // If multiple durations are defined, take the first
  64. transitionDuration = transitionDuration.split(',')[0];
  65. transitionDelay = transitionDelay.split(',')[0];
  66. return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
  67. };
  68. const triggerTransitionEnd = element => {
  69. element.dispatchEvent(new Event(TRANSITION_END));
  70. };
  71. const isElement = obj => {
  72. if (!obj || typeof obj !== 'object') {
  73. return false;
  74. }
  75. if (typeof obj.jquery !== 'undefined') {
  76. obj = obj[0];
  77. }
  78. return typeof obj.nodeType !== 'undefined';
  79. };
  80. const getElement = obj => {
  81. if (isElement(obj)) {
  82. // it's a jQuery object or a node element
  83. return obj.jquery ? obj[0] : obj;
  84. }
  85. if (typeof obj === 'string' && obj.length > 0) {
  86. return document.querySelector(obj);
  87. }
  88. return null;
  89. };
  90. const typeCheckConfig = (componentName, config, configTypes) => {
  91. Object.keys(configTypes).forEach(property => {
  92. const expectedTypes = configTypes[property];
  93. const value = config[property];
  94. const valueType = value && isElement(value) ? 'element' : toType(value);
  95. if (!new RegExp(expectedTypes).test(valueType)) {
  96. throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
  97. }
  98. });
  99. };
  100. const isVisible = element => {
  101. if (!isElement(element) || element.getClientRects().length === 0) {
  102. return false;
  103. }
  104. return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
  105. };
  106. const isDisabled = element => {
  107. if (!element || element.nodeType !== Node.ELEMENT_NODE) {
  108. return true;
  109. }
  110. if (element.classList.contains('disabled')) {
  111. return true;
  112. }
  113. if (typeof element.disabled !== 'undefined') {
  114. return element.disabled;
  115. }
  116. return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
  117. };
  118. /**
  119. * Trick to restart an element's animation
  120. *
  121. * @param {HTMLElement} element
  122. * @return void
  123. *
  124. * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
  125. */
  126. const reflow = element => {
  127. // eslint-disable-next-line no-unused-expressions
  128. element.offsetHeight;
  129. };
  130. const getjQuery = () => {
  131. const {
  132. jQuery
  133. } = window;
  134. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  135. return jQuery;
  136. }
  137. return null;
  138. };
  139. const DOMContentLoadedCallbacks = [];
  140. const onDOMContentLoaded = callback => {
  141. if (document.readyState === 'loading') {
  142. // add listener on the first call when the document is in loading state
  143. if (!DOMContentLoadedCallbacks.length) {
  144. document.addEventListener('DOMContentLoaded', () => {
  145. DOMContentLoadedCallbacks.forEach(callback => callback());
  146. });
  147. }
  148. DOMContentLoadedCallbacks.push(callback);
  149. } else {
  150. callback();
  151. }
  152. };
  153. const isRTL = () => document.documentElement.dir === 'rtl';
  154. const defineJQueryPlugin = plugin => {
  155. onDOMContentLoaded(() => {
  156. const $ = getjQuery();
  157. /* istanbul ignore if */
  158. if ($) {
  159. const name = plugin.NAME;
  160. const JQUERY_NO_CONFLICT = $.fn[name];
  161. $.fn[name] = plugin.jQueryInterface;
  162. $.fn[name].Constructor = plugin;
  163. $.fn[name].noConflict = () => {
  164. $.fn[name] = JQUERY_NO_CONFLICT;
  165. return plugin.jQueryInterface;
  166. };
  167. }
  168. });
  169. };
  170. const execute = callback => {
  171. if (typeof callback === 'function') {
  172. callback();
  173. }
  174. };
  175. const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
  176. if (!waitForTransition) {
  177. execute(callback);
  178. return;
  179. }
  180. const durationPadding = 5;
  181. const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
  182. let called = false;
  183. const handler = ({
  184. target
  185. }) => {
  186. if (target !== transitionElement) {
  187. return;
  188. }
  189. called = true;
  190. transitionElement.removeEventListener(TRANSITION_END, handler);
  191. execute(callback);
  192. };
  193. transitionElement.addEventListener(TRANSITION_END, handler);
  194. setTimeout(() => {
  195. if (!called) {
  196. triggerTransitionEnd(transitionElement);
  197. }
  198. }, emulatedDuration);
  199. };
  200. /**
  201. * --------------------------------------------------------------------------
  202. * Bootstrap (v5.1.3): util/scrollBar.js
  203. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  204. * --------------------------------------------------------------------------
  205. */
  206. const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
  207. const SELECTOR_STICKY_CONTENT = '.sticky-top';
  208. class ScrollBarHelper {
  209. constructor() {
  210. this._element = document.body;
  211. }
  212. getWidth() {
  213. // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
  214. const documentWidth = document.documentElement.clientWidth;
  215. return Math.abs(window.innerWidth - documentWidth);
  216. }
  217. hide() {
  218. const width = this.getWidth();
  219. this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width
  220. this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
  221. this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width);
  222. this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width);
  223. }
  224. _disableOverFlow() {
  225. this._saveInitialAttribute(this._element, 'overflow');
  226. this._element.style.overflow = 'hidden';
  227. }
  228. _setElementAttributes(selector, styleProp, callback) {
  229. const scrollbarWidth = this.getWidth();
  230. const manipulationCallBack = element => {
  231. if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
  232. return;
  233. }
  234. this._saveInitialAttribute(element, styleProp);
  235. const calculatedValue = window.getComputedStyle(element)[styleProp];
  236. element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`;
  237. };
  238. this._applyManipulationCallback(selector, manipulationCallBack);
  239. }
  240. reset() {
  241. this._resetElementAttributes(this._element, 'overflow');
  242. this._resetElementAttributes(this._element, 'paddingRight');
  243. this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight');
  244. this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight');
  245. }
  246. _saveInitialAttribute(element, styleProp) {
  247. const actualValue = element.style[styleProp];
  248. if (actualValue) {
  249. Manipulator__default.default.setDataAttribute(element, styleProp, actualValue);
  250. }
  251. }
  252. _resetElementAttributes(selector, styleProp) {
  253. const manipulationCallBack = element => {
  254. const value = Manipulator__default.default.getDataAttribute(element, styleProp);
  255. if (typeof value === 'undefined') {
  256. element.style.removeProperty(styleProp);
  257. } else {
  258. Manipulator__default.default.removeDataAttribute(element, styleProp);
  259. element.style[styleProp] = value;
  260. }
  261. };
  262. this._applyManipulationCallback(selector, manipulationCallBack);
  263. }
  264. _applyManipulationCallback(selector, callBack) {
  265. if (isElement(selector)) {
  266. callBack(selector);
  267. } else {
  268. SelectorEngine__default.default.find(selector, this._element).forEach(callBack);
  269. }
  270. }
  271. isOverflowing() {
  272. return this.getWidth() > 0;
  273. }
  274. }
  275. /**
  276. * --------------------------------------------------------------------------
  277. * Bootstrap (v5.1.3): util/backdrop.js
  278. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  279. * --------------------------------------------------------------------------
  280. */
  281. const Default$2 = {
  282. className: 'modal-backdrop',
  283. isVisible: true,
  284. // if false, we use the backdrop helper without adding any element to the dom
  285. isAnimated: false,
  286. rootElement: 'body',
  287. // give the choice to place backdrop under different elements
  288. clickCallback: null
  289. };
  290. const DefaultType$2 = {
  291. className: 'string',
  292. isVisible: 'boolean',
  293. isAnimated: 'boolean',
  294. rootElement: '(element|string)',
  295. clickCallback: '(function|null)'
  296. };
  297. const NAME$2 = 'backdrop';
  298. const CLASS_NAME_FADE$1 = 'fade';
  299. const CLASS_NAME_SHOW$1 = 'show';
  300. const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$2}`;
  301. class Backdrop {
  302. constructor(config) {
  303. this._config = this._getConfig(config);
  304. this._isAppended = false;
  305. this._element = null;
  306. }
  307. show(callback) {
  308. if (!this._config.isVisible) {
  309. execute(callback);
  310. return;
  311. }
  312. this._append();
  313. if (this._config.isAnimated) {
  314. reflow(this._getElement());
  315. }
  316. this._getElement().classList.add(CLASS_NAME_SHOW$1);
  317. this._emulateAnimation(() => {
  318. execute(callback);
  319. });
  320. }
  321. hide(callback) {
  322. if (!this._config.isVisible) {
  323. execute(callback);
  324. return;
  325. }
  326. this._getElement().classList.remove(CLASS_NAME_SHOW$1);
  327. this._emulateAnimation(() => {
  328. this.dispose();
  329. execute(callback);
  330. });
  331. } // Private
  332. _getElement() {
  333. if (!this._element) {
  334. const backdrop = document.createElement('div');
  335. backdrop.className = this._config.className;
  336. if (this._config.isAnimated) {
  337. backdrop.classList.add(CLASS_NAME_FADE$1);
  338. }
  339. this._element = backdrop;
  340. }
  341. return this._element;
  342. }
  343. _getConfig(config) {
  344. config = { ...Default$2,
  345. ...(typeof config === 'object' ? config : {})
  346. }; // use getElement() with the default "body" to get a fresh Element on each instantiation
  347. config.rootElement = getElement(config.rootElement);
  348. typeCheckConfig(NAME$2, config, DefaultType$2);
  349. return config;
  350. }
  351. _append() {
  352. if (this._isAppended) {
  353. return;
  354. }
  355. this._config.rootElement.append(this._getElement());
  356. EventHandler__default.default.on(this._getElement(), EVENT_MOUSEDOWN, () => {
  357. execute(this._config.clickCallback);
  358. });
  359. this._isAppended = true;
  360. }
  361. dispose() {
  362. if (!this._isAppended) {
  363. return;
  364. }
  365. EventHandler__default.default.off(this._element, EVENT_MOUSEDOWN);
  366. this._element.remove();
  367. this._isAppended = false;
  368. }
  369. _emulateAnimation(callback) {
  370. executeAfterTransition(callback, this._getElement(), this._config.isAnimated);
  371. }
  372. }
  373. /**
  374. * --------------------------------------------------------------------------
  375. * Bootstrap (v5.1.3): util/focustrap.js
  376. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  377. * --------------------------------------------------------------------------
  378. */
  379. const Default$1 = {
  380. trapElement: null,
  381. // The element to trap focus inside of
  382. autofocus: true
  383. };
  384. const DefaultType$1 = {
  385. trapElement: 'element',
  386. autofocus: 'boolean'
  387. };
  388. const NAME$1 = 'focustrap';
  389. const DATA_KEY$1 = 'bs.focustrap';
  390. const EVENT_KEY$1 = `.${DATA_KEY$1}`;
  391. const EVENT_FOCUSIN = `focusin${EVENT_KEY$1}`;
  392. const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$1}`;
  393. const TAB_KEY = 'Tab';
  394. const TAB_NAV_FORWARD = 'forward';
  395. const TAB_NAV_BACKWARD = 'backward';
  396. class FocusTrap {
  397. constructor(config) {
  398. this._config = this._getConfig(config);
  399. this._isActive = false;
  400. this._lastTabNavDirection = null;
  401. }
  402. activate() {
  403. const {
  404. trapElement,
  405. autofocus
  406. } = this._config;
  407. if (this._isActive) {
  408. return;
  409. }
  410. if (autofocus) {
  411. trapElement.focus();
  412. }
  413. EventHandler__default.default.off(document, EVENT_KEY$1); // guard against infinite focus loop
  414. EventHandler__default.default.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event));
  415. EventHandler__default.default.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));
  416. this._isActive = true;
  417. }
  418. deactivate() {
  419. if (!this._isActive) {
  420. return;
  421. }
  422. this._isActive = false;
  423. EventHandler__default.default.off(document, EVENT_KEY$1);
  424. } // Private
  425. _handleFocusin(event) {
  426. const {
  427. target
  428. } = event;
  429. const {
  430. trapElement
  431. } = this._config;
  432. if (target === document || target === trapElement || trapElement.contains(target)) {
  433. return;
  434. }
  435. const elements = SelectorEngine__default.default.focusableChildren(trapElement);
  436. if (elements.length === 0) {
  437. trapElement.focus();
  438. } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
  439. elements[elements.length - 1].focus();
  440. } else {
  441. elements[0].focus();
  442. }
  443. }
  444. _handleKeydown(event) {
  445. if (event.key !== TAB_KEY) {
  446. return;
  447. }
  448. this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;
  449. }
  450. _getConfig(config) {
  451. config = { ...Default$1,
  452. ...(typeof config === 'object' ? config : {})
  453. };
  454. typeCheckConfig(NAME$1, config, DefaultType$1);
  455. return config;
  456. }
  457. }
  458. /**
  459. * --------------------------------------------------------------------------
  460. * Bootstrap (v5.1.3): util/component-functions.js
  461. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  462. * --------------------------------------------------------------------------
  463. */
  464. const enableDismissTrigger = (component, method = 'hide') => {
  465. const clickEvent = `click.dismiss${component.EVENT_KEY}`;
  466. const name = component.NAME;
  467. EventHandler__default.default.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
  468. if (['A', 'AREA'].includes(this.tagName)) {
  469. event.preventDefault();
  470. }
  471. if (isDisabled(this)) {
  472. return;
  473. }
  474. const target = getElementFromSelector(this) || this.closest(`.${name}`);
  475. const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
  476. instance[method]();
  477. });
  478. };
  479. /**
  480. * --------------------------------------------------------------------------
  481. * Bootstrap (v5.1.3): modal.js
  482. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  483. * --------------------------------------------------------------------------
  484. */
  485. /**
  486. * ------------------------------------------------------------------------
  487. * Constants
  488. * ------------------------------------------------------------------------
  489. */
  490. const NAME = 'modal';
  491. const DATA_KEY = 'bs.modal';
  492. const EVENT_KEY = `.${DATA_KEY}`;
  493. const DATA_API_KEY = '.data-api';
  494. const ESCAPE_KEY = 'Escape';
  495. const Default = {
  496. backdrop: true,
  497. keyboard: true,
  498. focus: true
  499. };
  500. const DefaultType = {
  501. backdrop: '(boolean|string)',
  502. keyboard: 'boolean',
  503. focus: 'boolean'
  504. };
  505. const EVENT_HIDE = `hide${EVENT_KEY}`;
  506. const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
  507. const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
  508. const EVENT_SHOW = `show${EVENT_KEY}`;
  509. const EVENT_SHOWN = `shown${EVENT_KEY}`;
  510. const EVENT_RESIZE = `resize${EVENT_KEY}`;
  511. const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
  512. const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
  513. const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`;
  514. const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`;
  515. const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
  516. const CLASS_NAME_OPEN = 'modal-open';
  517. const CLASS_NAME_FADE = 'fade';
  518. const CLASS_NAME_SHOW = 'show';
  519. const CLASS_NAME_STATIC = 'modal-static';
  520. const OPEN_SELECTOR = '.modal.show';
  521. const SELECTOR_DIALOG = '.modal-dialog';
  522. const SELECTOR_MODAL_BODY = '.modal-body';
  523. const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
  524. /**
  525. * ------------------------------------------------------------------------
  526. * Class Definition
  527. * ------------------------------------------------------------------------
  528. */
  529. class Modal extends BaseComponent__default.default {
  530. constructor(element, config) {
  531. super(element);
  532. this._config = this._getConfig(config);
  533. this._dialog = SelectorEngine__default.default.findOne(SELECTOR_DIALOG, this._element);
  534. this._backdrop = this._initializeBackDrop();
  535. this._focustrap = this._initializeFocusTrap();
  536. this._isShown = false;
  537. this._ignoreBackdropClick = false;
  538. this._isTransitioning = false;
  539. this._scrollBar = new ScrollBarHelper();
  540. } // Getters
  541. static get Default() {
  542. return Default;
  543. }
  544. static get NAME() {
  545. return NAME;
  546. } // Public
  547. toggle(relatedTarget) {
  548. return this._isShown ? this.hide() : this.show(relatedTarget);
  549. }
  550. show(relatedTarget) {
  551. if (this._isShown || this._isTransitioning) {
  552. return;
  553. }
  554. const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW, {
  555. relatedTarget
  556. });
  557. if (showEvent.defaultPrevented) {
  558. return;
  559. }
  560. this._isShown = true;
  561. if (this._isAnimated()) {
  562. this._isTransitioning = true;
  563. }
  564. this._scrollBar.hide();
  565. document.body.classList.add(CLASS_NAME_OPEN);
  566. this._adjustDialog();
  567. this._setEscapeEvent();
  568. this._setResizeEvent();
  569. EventHandler__default.default.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
  570. EventHandler__default.default.one(this._element, EVENT_MOUSEUP_DISMISS, event => {
  571. if (event.target === this._element) {
  572. this._ignoreBackdropClick = true;
  573. }
  574. });
  575. });
  576. this._showBackdrop(() => this._showElement(relatedTarget));
  577. }
  578. hide() {
  579. if (!this._isShown || this._isTransitioning) {
  580. return;
  581. }
  582. const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
  583. if (hideEvent.defaultPrevented) {
  584. return;
  585. }
  586. this._isShown = false;
  587. const isAnimated = this._isAnimated();
  588. if (isAnimated) {
  589. this._isTransitioning = true;
  590. }
  591. this._setEscapeEvent();
  592. this._setResizeEvent();
  593. this._focustrap.deactivate();
  594. this._element.classList.remove(CLASS_NAME_SHOW);
  595. EventHandler__default.default.off(this._element, EVENT_CLICK_DISMISS);
  596. EventHandler__default.default.off(this._dialog, EVENT_MOUSEDOWN_DISMISS);
  597. this._queueCallback(() => this._hideModal(), this._element, isAnimated);
  598. }
  599. dispose() {
  600. [window, this._dialog].forEach(htmlElement => EventHandler__default.default.off(htmlElement, EVENT_KEY));
  601. this._backdrop.dispose();
  602. this._focustrap.deactivate();
  603. super.dispose();
  604. }
  605. handleUpdate() {
  606. this._adjustDialog();
  607. } // Private
  608. _initializeBackDrop() {
  609. return new Backdrop({
  610. isVisible: Boolean(this._config.backdrop),
  611. // 'static' option will be translated to true, and booleans will keep their value
  612. isAnimated: this._isAnimated()
  613. });
  614. }
  615. _initializeFocusTrap() {
  616. return new FocusTrap({
  617. trapElement: this._element
  618. });
  619. }
  620. _getConfig(config) {
  621. config = { ...Default,
  622. ...Manipulator__default.default.getDataAttributes(this._element),
  623. ...(typeof config === 'object' ? config : {})
  624. };
  625. typeCheckConfig(NAME, config, DefaultType);
  626. return config;
  627. }
  628. _showElement(relatedTarget) {
  629. const isAnimated = this._isAnimated();
  630. const modalBody = SelectorEngine__default.default.findOne(SELECTOR_MODAL_BODY, this._dialog);
  631. if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
  632. // Don't move modal's DOM position
  633. document.body.append(this._element);
  634. }
  635. this._element.style.display = 'block';
  636. this._element.removeAttribute('aria-hidden');
  637. this._element.setAttribute('aria-modal', true);
  638. this._element.setAttribute('role', 'dialog');
  639. this._element.scrollTop = 0;
  640. if (modalBody) {
  641. modalBody.scrollTop = 0;
  642. }
  643. if (isAnimated) {
  644. reflow(this._element);
  645. }
  646. this._element.classList.add(CLASS_NAME_SHOW);
  647. const transitionComplete = () => {
  648. if (this._config.focus) {
  649. this._focustrap.activate();
  650. }
  651. this._isTransitioning = false;
  652. EventHandler__default.default.trigger(this._element, EVENT_SHOWN, {
  653. relatedTarget
  654. });
  655. };
  656. this._queueCallback(transitionComplete, this._dialog, isAnimated);
  657. }
  658. _setEscapeEvent() {
  659. if (this._isShown) {
  660. EventHandler__default.default.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
  661. if (this._config.keyboard && event.key === ESCAPE_KEY) {
  662. event.preventDefault();
  663. this.hide();
  664. } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {
  665. this._triggerBackdropTransition();
  666. }
  667. });
  668. } else {
  669. EventHandler__default.default.off(this._element, EVENT_KEYDOWN_DISMISS);
  670. }
  671. }
  672. _setResizeEvent() {
  673. if (this._isShown) {
  674. EventHandler__default.default.on(window, EVENT_RESIZE, () => this._adjustDialog());
  675. } else {
  676. EventHandler__default.default.off(window, EVENT_RESIZE);
  677. }
  678. }
  679. _hideModal() {
  680. this._element.style.display = 'none';
  681. this._element.setAttribute('aria-hidden', true);
  682. this._element.removeAttribute('aria-modal');
  683. this._element.removeAttribute('role');
  684. this._isTransitioning = false;
  685. this._backdrop.hide(() => {
  686. document.body.classList.remove(CLASS_NAME_OPEN);
  687. this._resetAdjustments();
  688. this._scrollBar.reset();
  689. EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
  690. });
  691. }
  692. _showBackdrop(callback) {
  693. EventHandler__default.default.on(this._element, EVENT_CLICK_DISMISS, event => {
  694. if (this._ignoreBackdropClick) {
  695. this._ignoreBackdropClick = false;
  696. return;
  697. }
  698. if (event.target !== event.currentTarget) {
  699. return;
  700. }
  701. if (this._config.backdrop === true) {
  702. this.hide();
  703. } else if (this._config.backdrop === 'static') {
  704. this._triggerBackdropTransition();
  705. }
  706. });
  707. this._backdrop.show(callback);
  708. }
  709. _isAnimated() {
  710. return this._element.classList.contains(CLASS_NAME_FADE);
  711. }
  712. _triggerBackdropTransition() {
  713. const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE_PREVENTED);
  714. if (hideEvent.defaultPrevented) {
  715. return;
  716. }
  717. const {
  718. classList,
  719. scrollHeight,
  720. style
  721. } = this._element;
  722. const isModalOverflowing = scrollHeight > document.documentElement.clientHeight; // return if the following background transition hasn't yet completed
  723. if (!isModalOverflowing && style.overflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
  724. return;
  725. }
  726. if (!isModalOverflowing) {
  727. style.overflowY = 'hidden';
  728. }
  729. classList.add(CLASS_NAME_STATIC);
  730. this._queueCallback(() => {
  731. classList.remove(CLASS_NAME_STATIC);
  732. if (!isModalOverflowing) {
  733. this._queueCallback(() => {
  734. style.overflowY = '';
  735. }, this._dialog);
  736. }
  737. }, this._dialog);
  738. this._element.focus();
  739. } // ----------------------------------------------------------------------
  740. // the following methods are used to handle overflowing modals
  741. // ----------------------------------------------------------------------
  742. _adjustDialog() {
  743. const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
  744. const scrollbarWidth = this._scrollBar.getWidth();
  745. const isBodyOverflowing = scrollbarWidth > 0;
  746. if (!isBodyOverflowing && isModalOverflowing && !isRTL() || isBodyOverflowing && !isModalOverflowing && isRTL()) {
  747. this._element.style.paddingLeft = `${scrollbarWidth}px`;
  748. }
  749. if (isBodyOverflowing && !isModalOverflowing && !isRTL() || !isBodyOverflowing && isModalOverflowing && isRTL()) {
  750. this._element.style.paddingRight = `${scrollbarWidth}px`;
  751. }
  752. }
  753. _resetAdjustments() {
  754. this._element.style.paddingLeft = '';
  755. this._element.style.paddingRight = '';
  756. } // Static
  757. static jQueryInterface(config, relatedTarget) {
  758. return this.each(function () {
  759. const data = Modal.getOrCreateInstance(this, config);
  760. if (typeof config !== 'string') {
  761. return;
  762. }
  763. if (typeof data[config] === 'undefined') {
  764. throw new TypeError(`No method named "${config}"`);
  765. }
  766. data[config](relatedTarget);
  767. });
  768. }
  769. }
  770. /**
  771. * ------------------------------------------------------------------------
  772. * Data Api implementation
  773. * ------------------------------------------------------------------------
  774. */
  775. EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  776. const target = getElementFromSelector(this);
  777. if (['A', 'AREA'].includes(this.tagName)) {
  778. event.preventDefault();
  779. }
  780. EventHandler__default.default.one(target, EVENT_SHOW, showEvent => {
  781. if (showEvent.defaultPrevented) {
  782. // only register focus restorer if modal will actually get shown
  783. return;
  784. }
  785. EventHandler__default.default.one(target, EVENT_HIDDEN, () => {
  786. if (isVisible(this)) {
  787. this.focus();
  788. }
  789. });
  790. }); // avoid conflict when clicking moddal toggler while another one is open
  791. const allReadyOpen = SelectorEngine__default.default.findOne(OPEN_SELECTOR);
  792. if (allReadyOpen) {
  793. Modal.getInstance(allReadyOpen).hide();
  794. }
  795. const data = Modal.getOrCreateInstance(target);
  796. data.toggle(this);
  797. });
  798. enableDismissTrigger(Modal);
  799. /**
  800. * ------------------------------------------------------------------------
  801. * jQuery
  802. * ------------------------------------------------------------------------
  803. * add .Modal to jQuery only if jQuery is present
  804. */
  805. defineJQueryPlugin(Modal);
  806. return Modal;
  807. }));
  808. //# sourceMappingURL=modal.js.map