diff options
Diffstat (limited to 'vendor/twbs/bootstrap/js/src/modal.js')
-rw-r--r-- | vendor/twbs/bootstrap/js/src/modal.js | 252 |
1 files changed, 94 insertions, 158 deletions
diff --git a/vendor/twbs/bootstrap/js/src/modal.js b/vendor/twbs/bootstrap/js/src/modal.js index 00df5c482..3e990e7cc 100644 --- a/vendor/twbs/bootstrap/js/src/modal.js +++ b/vendor/twbs/bootstrap/js/src/modal.js @@ -1,20 +1,12 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.1.3): modal.js + * Bootstrap (v5.2.0): modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import { - defineJQueryPlugin, - getElementFromSelector, - isRTL, - isVisible, - reflow, - typeCheckConfig -} from './util/index' +import { defineJQueryPlugin, getElementFromSelector, isRTL, isVisible, reflow } from './util/index' import EventHandler from './dom/event-handler' -import Manipulator from './dom/manipulator' import SelectorEngine from './dom/selector-engine' import ScrollBarHelper from './util/scrollbar' import BaseComponent from './base-component' @@ -23,9 +15,7 @@ import FocusTrap from './util/focustrap' import { enableDismissTrigger } from './util/component-functions' /** - * ------------------------------------------------------------------------ * Constants - * ------------------------------------------------------------------------ */ const NAME = 'modal' @@ -34,28 +24,14 @@ const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' const ESCAPE_KEY = 'Escape' -const Default = { - backdrop: true, - keyboard: true, - focus: true -} - -const DefaultType = { - backdrop: '(boolean|string)', - keyboard: 'boolean', - focus: 'boolean' -} - const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` const EVENT_SHOW = `show${EVENT_KEY}` const EVENT_SHOWN = `shown${EVENT_KEY}` const EVENT_RESIZE = `resize${EVENT_KEY}` -const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}` -const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` -const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}` const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}` +const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_OPEN = 'modal-open' @@ -68,38 +44,50 @@ const SELECTOR_DIALOG = '.modal-dialog' const SELECTOR_MODAL_BODY = '.modal-body' const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]' +const Default = { + backdrop: true, + focus: true, + keyboard: true +} + +const DefaultType = { + backdrop: '(boolean|string)', + focus: 'boolean', + keyboard: 'boolean' +} + /** - * ------------------------------------------------------------------------ - * Class Definition - * ------------------------------------------------------------------------ + * Class definition */ class Modal extends BaseComponent { constructor(element, config) { - super(element) + super(element, config) - this._config = this._getConfig(config) this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element) this._backdrop = this._initializeBackDrop() this._focustrap = this._initializeFocusTrap() this._isShown = false - this._ignoreBackdropClick = false this._isTransitioning = false this._scrollBar = new ScrollBarHelper() + + this._addEventListeners() } // Getters - static get Default() { return Default } + static get DefaultType() { + return DefaultType + } + static get NAME() { return NAME } // Public - toggle(relatedTarget) { return this._isShown ? this.hide() : this.show(relatedTarget) } @@ -118,10 +106,7 @@ class Modal extends BaseComponent { } this._isShown = true - - if (this._isAnimated()) { - this._isTransitioning = true - } + this._isTransitioning = true this._scrollBar.hide() @@ -129,18 +114,7 @@ class Modal extends BaseComponent { this._adjustDialog() - this._setEscapeEvent() - this._setResizeEvent() - - EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => { - EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => { - if (event.target === this._element) { - this._ignoreBackdropClick = true - } - }) - }) - - this._showBackdrop(() => this._showElement(relatedTarget)) + this._backdrop.show(() => this._showElement(relatedTarget)) } hide() { @@ -155,28 +129,18 @@ class Modal extends BaseComponent { } this._isShown = false - const isAnimated = this._isAnimated() - - if (isAnimated) { - this._isTransitioning = true - } - - this._setEscapeEvent() - this._setResizeEvent() - + this._isTransitioning = true this._focustrap.deactivate() this._element.classList.remove(CLASS_NAME_SHOW) - EventHandler.off(this._element, EVENT_CLICK_DISMISS) - EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS) - - this._queueCallback(() => this._hideModal(), this._element, isAnimated) + this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()) } dispose() { - [window, this._dialog] - .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY)) + for (const htmlElement of [window, this._dialog]) { + EventHandler.off(htmlElement, EVENT_KEY) + } this._backdrop.dispose() this._focustrap.deactivate() @@ -188,10 +152,9 @@ class Modal extends BaseComponent { } // Private - _initializeBackDrop() { return new Backdrop({ - isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value + isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value, isAnimated: this._isAnimated() }) } @@ -202,22 +165,9 @@ class Modal extends BaseComponent { }) } - _getConfig(config) { - config = { - ...Default, - ...Manipulator.getDataAttributes(this._element), - ...(typeof config === 'object' ? config : {}) - } - typeCheckConfig(NAME, config, DefaultType) - return config - } - _showElement(relatedTarget) { - const isAnimated = this._isAnimated() - const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) - - if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { - // Don't move modal's DOM position + // try to append dynamic modal + if (!document.body.contains(this._element)) { document.body.append(this._element) } @@ -227,13 +177,12 @@ class Modal extends BaseComponent { this._element.setAttribute('role', 'dialog') this._element.scrollTop = 0 + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) if (modalBody) { modalBody.scrollTop = 0 } - if (isAnimated) { - reflow(this._element) - } + reflow(this._element) this._element.classList.add(CLASS_NAME_SHOW) @@ -248,30 +197,44 @@ class Modal extends BaseComponent { }) } - this._queueCallback(transitionComplete, this._dialog, isAnimated) + this._queueCallback(transitionComplete, this._dialog, this._isAnimated()) } - _setEscapeEvent() { - if (this._isShown) { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { - if (this._config.keyboard && event.key === ESCAPE_KEY) { - event.preventDefault() - this.hide() - } else if (!this._config.keyboard && event.key === ESCAPE_KEY) { - this._triggerBackdropTransition() - } - }) - } else { - EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS) - } - } + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + if (event.key !== ESCAPE_KEY) { + return + } - _setResizeEvent() { - if (this._isShown) { - EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog()) - } else { - EventHandler.off(window, EVENT_RESIZE) - } + if (this._config.keyboard) { + event.preventDefault() + this.hide() + return + } + + this._triggerBackdropTransition() + }) + + EventHandler.on(window, EVENT_RESIZE, () => { + if (this._isShown && !this._isTransitioning) { + this._adjustDialog() + } + }) + + EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { + if (event.target !== event.currentTarget) { // click is inside modal-dialog + return + } + + if (this._config.backdrop === 'static') { + this._triggerBackdropTransition() + return + } + + if (this._config.backdrop) { + this.hide() + } + }) } _hideModal() { @@ -280,6 +243,7 @@ class Modal extends BaseComponent { this._element.removeAttribute('aria-modal') this._element.removeAttribute('role') this._isTransitioning = false + this._backdrop.hide(() => { document.body.classList.remove(CLASS_NAME_OPEN) this._resetAdjustments() @@ -288,27 +252,6 @@ class Modal extends BaseComponent { }) } - _showBackdrop(callback) { - EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => { - if (this._ignoreBackdropClick) { - this._ignoreBackdropClick = false - return - } - - if (event.target !== event.currentTarget) { - return - } - - if (this._config.backdrop === true) { - this.hide() - } else if (this._config.backdrop === 'static') { - this._triggerBackdropTransition() - } - }) - - this._backdrop.show(callback) - } - _isAnimated() { return this._element.classList.contains(CLASS_NAME_FADE) } @@ -319,46 +262,45 @@ class Modal extends BaseComponent { return } - const { classList, scrollHeight, style } = this._element - const isModalOverflowing = scrollHeight > document.documentElement.clientHeight - + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight + const initialOverflowY = this._element.style.overflowY // return if the following background transition hasn't yet completed - if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) { + if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { return } if (!isModalOverflowing) { - style.overflowY = 'hidden' + this._element.style.overflowY = 'hidden' } - classList.add(CLASS_NAME_STATIC) + this._element.classList.add(CLASS_NAME_STATIC) this._queueCallback(() => { - classList.remove(CLASS_NAME_STATIC) - if (!isModalOverflowing) { - this._queueCallback(() => { - style.overflowY = '' - }, this._dialog) - } + this._element.classList.remove(CLASS_NAME_STATIC) + this._queueCallback(() => { + this._element.style.overflowY = initialOverflowY + }, this._dialog) }, this._dialog) this._element.focus() } - // ---------------------------------------------------------------------- - // the following methods are used to handle overflowing modals - // ---------------------------------------------------------------------- + /** + * The following methods are used to handle overflowing modals + */ _adjustDialog() { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight const scrollbarWidth = this._scrollBar.getWidth() const isBodyOverflowing = scrollbarWidth > 0 - if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) { - this._element.style.paddingLeft = `${scrollbarWidth}px` + if (isBodyOverflowing && !isModalOverflowing) { + const property = isRTL() ? 'paddingLeft' : 'paddingRight' + this._element.style[property] = `${scrollbarWidth}px` } - if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) { - this._element.style.paddingRight = `${scrollbarWidth}px` + if (!isBodyOverflowing && isModalOverflowing) { + const property = isRTL() ? 'paddingRight' : 'paddingLeft' + this._element.style[property] = `${scrollbarWidth}px` } } @@ -368,7 +310,6 @@ class Modal extends BaseComponent { } // Static - static jQueryInterface(config, relatedTarget) { return this.each(function () { const data = Modal.getOrCreateInstance(this, config) @@ -387,9 +328,7 @@ class Modal extends BaseComponent { } /** - * ------------------------------------------------------------------------ - * Data Api implementation - * ------------------------------------------------------------------------ + * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { @@ -412,10 +351,10 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) }) - // avoid conflict when clicking moddal toggler while another one is open - const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) - if (allReadyOpen) { - Modal.getInstance(allReadyOpen).hide() + // avoid conflict when clicking modal toggler while another one is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) + if (alreadyOpen) { + Modal.getInstance(alreadyOpen).hide() } const data = Modal.getOrCreateInstance(target) @@ -426,10 +365,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( enableDismissTrigger(Modal) /** - * ------------------------------------------------------------------------ * jQuery - * ------------------------------------------------------------------------ - * add .Modal to jQuery only if jQuery is present */ defineJQueryPlugin(Modal) |