Skip to content

Commit

Permalink
Decouple BackDrop from modal (#32439)
Browse files Browse the repository at this point in the history
* Create backdrop.js util

* revert breaking changes

remove PromiseTimout usage

revert class name

* one more test | change bundlewatch.config

* add config obj to backdrop helper | tests for rootElement | use transitionend helper

* Minor tweaks — Renaming

Co-authored-by: Rohit Sharma <rohit2sharma95@gmail.com>
  • Loading branch information
GeoSot and rohit2sharma95 committed Apr 14, 2021
1 parent 0122e02 commit 80085a1
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 69 deletions.
8 changes: 4 additions & 4 deletions .bundlewatch.config.json
Expand Up @@ -34,23 +34,23 @@
},
{
"path": "./dist/js/bootstrap.bundle.js",
"maxSize": "41 kB"
"maxSize": "41.25 kB"
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
"maxSize": "22 kB"
"maxSize": "22.25 kB"
},
{
"path": "./dist/js/bootstrap.esm.js",
"maxSize": "27 kB"
},
{
"path": "./dist/js/bootstrap.esm.min.js",
"maxSize": "18 kB"
"maxSize": "18.25 kB"
},
{
"path": "./dist/js/bootstrap.js",
"maxSize": "27 kB"
"maxSize": "27.25 kB"
},
{
"path": "./dist/js/bootstrap.min.js",
Expand Down
6 changes: 4 additions & 2 deletions build/build-plugins.js
Expand Up @@ -65,7 +65,8 @@ const getConfigByPluginKey = pluginKey => {
pluginKey === 'EventHandler' ||
pluginKey === 'SelectorEngine' ||
pluginKey === 'Util' ||
pluginKey === 'Sanitizer'
pluginKey === 'Sanitizer' ||
pluginKey === 'Backdrop'
) {
return {
external: []
Expand Down Expand Up @@ -133,7 +134,8 @@ const getConfigByPluginKey = pluginKey => {

const utilObjects = new Set([
'Util',
'Sanitizer'
'Sanitizer',
'Backdrop'
])

const domObjects = new Set([
Expand Down
84 changes: 22 additions & 62 deletions js/src/modal.js
Expand Up @@ -20,6 +20,7 @@ import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
import { getWidth as getScrollBarWidth, hide as scrollBarHide, reset as scrollBarReset } from './util/scrollbar'
import BaseComponent from './base-component'
import Backdrop from './util/backdrop'

/**
* ------------------------------------------------------------------------
Expand Down Expand Up @@ -58,7 +59,6 @@ const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`
const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`

const CLASS_NAME_BACKDROP = 'modal-backdrop'
const CLASS_NAME_OPEN = 'modal-open'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
Expand All @@ -81,7 +81,7 @@ class Modal extends BaseComponent {

this._config = this._getConfig(config)
this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)
this._backdrop = null
this._backdrop = this._initializeBackDrop()
this._isShown = false
this._ignoreBackdropClick = false
this._isTransitioning = false
Expand Down Expand Up @@ -201,6 +201,7 @@ class Modal extends BaseComponent {

this._config = null
this._dialog = null
this._backdrop.dispose()
this._backdrop = null
this._isShown = null
this._ignoreBackdropClick = null
Expand All @@ -213,6 +214,13 @@ 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
isAnimated: this._isAnimated()
})
}

_getConfig(config) {
config = {
...Default,
Expand Down Expand Up @@ -313,81 +321,33 @@ class Modal extends BaseComponent {
this._element.removeAttribute('aria-modal')
this._element.removeAttribute('role')
this._isTransitioning = false
this._showBackdrop(() => {
this._backdrop.hide(() => {
document.body.classList.remove(CLASS_NAME_OPEN)
this._resetAdjustments()
scrollBarReset()
EventHandler.trigger(this._element, EVENT_HIDDEN)
})
}

_removeBackdrop() {
this._backdrop.parentNode.removeChild(this._backdrop)
this._backdrop = null
}

_showBackdrop(callback) {
const isAnimated = this._isAnimated()
if (this._isShown && this._config.backdrop) {
this._backdrop = document.createElement('div')
this._backdrop.className = CLASS_NAME_BACKDROP

if (isAnimated) {
this._backdrop.classList.add(CLASS_NAME_FADE)
}

document.body.appendChild(this._backdrop)

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 === 'static') {
this._triggerBackdropTransition()
} else {
this.hide()
}
})

if (isAnimated) {
reflow(this._backdrop)
EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
if (this._ignoreBackdropClick) {
this._ignoreBackdropClick = false
return
}

this._backdrop.classList.add(CLASS_NAME_SHOW)

if (!isAnimated) {
callback()
if (event.target !== event.currentTarget) {
return
}

const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)

EventHandler.one(this._backdrop, 'transitionend', callback)
emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
} else if (!this._isShown && this._backdrop) {
this._backdrop.classList.remove(CLASS_NAME_SHOW)

const callbackRemove = () => {
this._removeBackdrop()
callback()
if (this._config.backdrop === true) {
this.hide()
} else if (this._config.backdrop === 'static') {
this._triggerBackdropTransition()
}
})

if (isAnimated) {
const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
EventHandler.one(this._backdrop, 'transitionend', callbackRemove)
emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
} else {
callbackRemove()
}
} else {
callback()
}
this._backdrop.show(callback)
}

_isAnimated() {
Expand Down
123 changes: 123 additions & 0 deletions js/src/util/backdrop.js
@@ -0,0 +1,123 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.0.0-beta3): util/backdrop.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/

import EventHandler from '../dom/event-handler'
import { emulateTransitionEnd, execute, getTransitionDurationFromElement, reflow, typeCheckConfig } from './index'

const Default = {
isVisible: true, // if false, we use the backdrop helper without adding any element to the dom
isAnimated: false,
rootElement: document.body // give the choice to place backdrop under different elements
}

const DefaultType = {
isVisible: 'boolean',
isAnimated: 'boolean',
rootElement: 'element'
}
const NAME = 'backdrop'
const CLASS_NAME_BACKDROP = 'modal-backdrop'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'

class Backdrop {
constructor(config) {
this._config = this._getConfig(config)
this._isAppended = false
this._element = null
}

show(callback) {
if (!this._config.isVisible) {
execute(callback)
return
}

this._append()

if (this._config.isAnimated) {
reflow(this._getElement())
}

this._getElement().classList.add(CLASS_NAME_SHOW)

this._emulateAnimation(() => {
execute(callback)
})
}

hide(callback) {
if (!this._config.isVisible) {
execute(callback)
return
}

this._getElement().classList.remove(CLASS_NAME_SHOW)

this._emulateAnimation(() => {
this.dispose()
execute(callback)
})
}

// Private

_getElement() {
if (!this._element) {
const backdrop = document.createElement('div')
backdrop.className = CLASS_NAME_BACKDROP
if (this._config.isAnimated) {
backdrop.classList.add(CLASS_NAME_FADE)
}

this._element = backdrop
}

return this._element
}

_getConfig(config) {
config = {
...Default,
...(typeof config === 'object' ? config : {})
}
typeCheckConfig(NAME, config, DefaultType)
return config
}

_append() {
if (this._isAppended) {
return
}

this._config.rootElement.appendChild(this._getElement())

this._isAppended = true
}

dispose() {
if (!this._isAppended) {
return
}

this._getElement().parentNode.removeChild(this._element)
this._isAppended = false
}

_emulateAnimation(callback) {
if (!this._config.isAnimated) {
execute(callback)
return
}

const backdropTransitionDuration = getTransitionDurationFromElement(this._getElement())
EventHandler.one(this._getElement(), 'transitionend', () => execute(callback))
emulateTransitionEnd(this._getElement(), backdropTransitionDuration)
}
}

export default Backdrop
9 changes: 8 additions & 1 deletion js/src/util/index.js
Expand Up @@ -230,6 +230,12 @@ const defineJQueryPlugin = (name, plugin) => {
})
}

const execute = callback => {
if (typeof callback === 'function') {
callback()
}
}

export {
getUID,
getSelectorFromElement,
Expand All @@ -247,5 +253,6 @@ export {
getjQuery,
onDOMContentLoaded,
isRTL,
defineJQueryPlugin
defineJQueryPlugin,
execute
}

0 comments on commit 80085a1

Please sign in to comment.