aboutsummaryrefslogtreecommitdiff
path: root/js/src/modal.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/modal.js')
-rw-r--r--js/src/modal.js152
1 files changed, 62 insertions, 90 deletions
diff --git a/js/src/modal.js b/js/src/modal.js
index b8b144774..dd61649ec 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -1,26 +1,20 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): modal.js
+ * Bootstrap modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import Backdrop from './util/backdrop.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import FocusTrap from './util/focustrap.js'
import {
- defineJQueryPlugin,
- getElementFromSelector,
- isRTL,
- isVisible,
- reflow,
- typeCheckConfig
-} 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'
-import Backdrop from './util/backdrop'
-import FocusTrap from './util/focustrap'
-import { enableDismissTrigger } from './util/component-functions'
+ defineJQueryPlugin, isRTL, isVisible, reflow
+} from './util/index.js'
+import ScrollBarHelper from './util/scrollbar.js'
/**
* Constants
@@ -39,6 +33,7 @@ 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_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}`
@@ -54,14 +49,14 @@ const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]'
const Default = {
backdrop: true,
- keyboard: true,
- focus: true
+ focus: true,
+ keyboard: true
}
const DefaultType = {
backdrop: '(boolean|string)',
- keyboard: 'boolean',
- focus: 'boolean'
+ focus: 'boolean',
+ keyboard: 'boolean'
}
/**
@@ -70,15 +65,16 @@ const DefaultType = {
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._isTransitioning = false
this._scrollBar = new ScrollBarHelper()
+
+ this._addEventListeners()
}
// Getters
@@ -86,6 +82,10 @@ class Modal extends BaseComponent {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
static get NAME() {
return NAME
}
@@ -117,10 +117,7 @@ class Modal extends BaseComponent {
this._adjustDialog()
- this._toggleEscapeEventListener(true)
- this._toggleResizeEventListener(true)
-
- this._showBackdrop(() => this._showElement(relatedTarget))
+ this._backdrop.show(() => this._showElement(relatedTarget))
}
hide() {
@@ -136,10 +133,6 @@ class Modal extends BaseComponent {
this._isShown = false
this._isTransitioning = true
-
- this._toggleEscapeEventListener(false)
- this._toggleResizeEventListener(false)
-
this._focustrap.deactivate()
this._element.classList.remove(CLASS_NAME_SHOW)
@@ -148,12 +141,12 @@ class Modal extends BaseComponent {
}
dispose() {
- for (const htmlElement of [window, this._dialog]) {
- EventHandler.off(htmlElement, EVENT_KEY)
- }
+ EventHandler.off(window, EVENT_KEY)
+ EventHandler.off(this._dialog, EVENT_KEY)
this._backdrop.dispose()
this._focustrap.deactivate()
+
super.dispose()
}
@@ -164,7 +157,7 @@ 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()
})
}
@@ -175,16 +168,6 @@ class Modal extends BaseComponent {
})
}
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
- }
- typeCheckConfig(NAME, config, DefaultType)
- return config
- }
-
_showElement(relatedTarget) {
// try to append dynamic modal
if (!document.body.contains(this._element)) {
@@ -220,34 +203,43 @@ class Modal extends BaseComponent {
this._queueCallback(transitionComplete, this._dialog, this._isAnimated())
}
- _toggleEscapeEventListener(enable) {
- if (!enable) {
- EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)
- return
- }
-
+ _addEventListeners() {
EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
if (event.key !== ESCAPE_KEY) {
return
}
if (this._config.keyboard) {
- event.preventDefault()
this.hide()
return
}
this._triggerBackdropTransition()
})
- }
- _toggleResizeEventListener(enable) {
- if (enable) {
- EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())
- return
- }
+ EventHandler.on(window, EVENT_RESIZE, () => {
+ if (this._isShown && !this._isTransitioning) {
+ this._adjustDialog()
+ }
+ })
- EventHandler.off(window, EVENT_RESIZE)
+ EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {
+ // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
+ EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {
+ if (this._element !== event.target || this._element !== event2.target) {
+ return
+ }
+
+ if (this._config.backdrop === 'static') {
+ this._triggerBackdropTransition()
+ return
+ }
+
+ if (this._config.backdrop) {
+ this.hide()
+ }
+ })
+ })
}
_hideModal() {
@@ -265,25 +257,6 @@ class Modal extends BaseComponent {
})
}
- _showBackdrop(callback) {
- EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
- if (event.target !== event.currentTarget) {
- return
- }
-
- if (this._config.backdrop === true) {
- this.hide()
- return
- }
-
- if (this._config.backdrop === 'static') {
- this._triggerBackdropTransition()
- }
- })
-
- this._backdrop.show(callback)
- }
-
_isAnimated() {
return this._element.classList.contains(CLASS_NAME_FADE)
}
@@ -294,23 +267,22 @@ class Modal extends BaseComponent {
return
}
- const { classList, scrollHeight, style } = this._element
- const isModalOverflowing = scrollHeight > document.documentElement.clientHeight
- const initialOverflowY = style.overflowY
+ 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 (initialOverflowY === '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)
+ this._element.classList.remove(CLASS_NAME_STATIC)
this._queueCallback(() => {
- style.overflowY = initialOverflowY
+ this._element.style.overflowY = initialOverflowY
}, this._dialog)
}, this._dialog)
@@ -365,7 +337,7 @@ class Modal extends BaseComponent {
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this)
+ const target = SelectorEngine.getElementFromSelector(this)
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
@@ -384,10 +356,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)