diff options
| author | GeoSot <[email protected]> | 2021-04-19 08:20:25 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-04-19 08:20:25 +0300 |
| commit | a9d7a62658c5d93dcba5ed5fc47d84f3ddd3e0a3 (patch) | |
| tree | abc7fcf44b0dabe61ea03933d1b1c6308e2619ae /js/src/offcanvas.js | |
| parent | df8131a1f88e62f6bc8ae1c669bc0e534965bf1a (diff) | |
| download | bootstrap-a9d7a62658c5d93dcba5ed5fc47d84f3ddd3e0a3.tar.xz bootstrap-a9d7a62658c5d93dcba5ed5fc47d84f3ddd3e0a3.zip | |
Use the backdrop util in offcanvas, enforcing consistency (#33545)
* respect /share modal's backdrop functionality, keeping consistency
* listen click events over backdrop (only) and trigger `hide()` without add/remove event tricks
* achieve to hide foreign open offcanvas instances without glitches `if (allReadyOpen && allReadyOpen !== target)`, in case another is going to be open, when user clicks on trigger button
Diffstat (limited to 'js/src/offcanvas.js')
| -rw-r--r-- | js/src/offcanvas.js | 60 |
1 files changed, 33 insertions, 27 deletions
diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js index 02b0b58a9..2b6335b39 100644 --- a/js/src/offcanvas.js +++ b/js/src/offcanvas.js @@ -7,8 +7,8 @@ import { defineJQueryPlugin, + emulateTransitionEnd, getElementFromSelector, - getSelectorFromElement, getTransitionDurationFromElement, isDisabled, isVisible, @@ -20,6 +20,7 @@ import EventHandler from './dom/event-handler' import BaseComponent from './base-component' import SelectorEngine from './dom/selector-engine' import Manipulator from './dom/manipulator' +import Backdrop from './util/backdrop' /** * ------------------------------------------------------------------------ @@ -46,11 +47,8 @@ const DefaultType = { scroll: 'boolean' } -const CLASS_NAME_BACKDROP_BODY = 'offcanvas-backdrop' const CLASS_NAME_SHOW = 'show' -const CLASS_NAME_TOGGLING = 'offcanvas-toggling' const OPEN_SELECTOR = '.offcanvas.show' -const ACTIVE_SELECTOR = `${OPEN_SELECTOR}, .${CLASS_NAME_TOGGLING}` const EVENT_SHOW = `show${EVENT_KEY}` const EVENT_SHOWN = `shown${EVENT_KEY}` @@ -59,6 +57,7 @@ const EVENT_HIDDEN = `hidden${EVENT_KEY}` const EVENT_FOCUSIN = `focusin${EVENT_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}` +const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}` const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="offcanvas"]' const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="offcanvas"]' @@ -75,6 +74,7 @@ class Offcanvas extends BaseComponent { this._config = this._getConfig(config) this._isShown = false + this._backdrop = this._initializeBackDrop() this._addEventListeners() } @@ -108,27 +108,25 @@ class Offcanvas extends BaseComponent { this._isShown = true this._element.style.visibility = 'visible' - if (this._config.backdrop) { - document.body.classList.add(CLASS_NAME_BACKDROP_BODY) - } + this._backdrop.show() if (!this._config.scroll) { scrollBarHide() } - this._element.classList.add(CLASS_NAME_TOGGLING) this._element.removeAttribute('aria-hidden') this._element.setAttribute('aria-modal', true) this._element.setAttribute('role', 'dialog') this._element.classList.add(CLASS_NAME_SHOW) const completeCallBack = () => { - this._element.classList.remove(CLASS_NAME_TOGGLING) EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget }) this._enforceFocusOnElement(this._element) } - setTimeout(completeCallBack, getTransitionDurationFromElement(this._element)) + const transitionDuration = getTransitionDurationFromElement(this._element) + EventHandler.one(this._element, 'transitionend', completeCallBack) + emulateTransitionEnd(this._element, transitionDuration) } hide() { @@ -142,11 +140,11 @@ class Offcanvas extends BaseComponent { return } - this._element.classList.add(CLASS_NAME_TOGGLING) EventHandler.off(document, EVENT_FOCUSIN) this._element.blur() this._isShown = false this._element.classList.remove(CLASS_NAME_SHOW) + this._backdrop.hide() const completeCallback = () => { this._element.setAttribute('aria-hidden', true) @@ -154,19 +152,25 @@ class Offcanvas extends BaseComponent { this._element.removeAttribute('role') this._element.style.visibility = 'hidden' - if (this._config.backdrop) { - document.body.classList.remove(CLASS_NAME_BACKDROP_BODY) - } - if (!this._config.scroll) { scrollBarReset() } EventHandler.trigger(this._element, EVENT_HIDDEN) - this._element.classList.remove(CLASS_NAME_TOGGLING) } - setTimeout(completeCallback, getTransitionDurationFromElement(this._element)) + const transitionDuration = getTransitionDurationFromElement(this._element) + EventHandler.one(this._element, 'transitionend', completeCallback) + emulateTransitionEnd(this._element, transitionDuration) + } + + dispose() { + this._backdrop.dispose() + super.dispose() + EventHandler.off(document, EVENT_FOCUSIN) + + this._config = null + this._backdrop = null } // Private @@ -181,6 +185,15 @@ class Offcanvas extends BaseComponent { return config } + _initializeBackDrop() { + return new Backdrop({ + isVisible: this._config.backdrop, + isAnimated: true, + rootElement: this._element.parentNode, + clickCallback: () => this.hide() + }) + } + _enforceFocusOnElement(element) { EventHandler.off(document, EVENT_FOCUSIN) // guard against infinite focus loop EventHandler.on(document, EVENT_FOCUSIN, event => { @@ -196,18 +209,11 @@ class Offcanvas extends BaseComponent { _addEventListeners() { EventHandler.on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide()) - EventHandler.on(document, 'keydown', event => { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { if (this._config.keyboard && event.key === ESCAPE_KEY) { this.hide() } }) - - EventHandler.on(document, EVENT_CLICK_DATA_API, event => { - const target = SelectorEngine.findOne(getSelectorFromElement(event.target)) - if (!this._element.contains(event.target) && target !== this._element) { - this.hide() - } - }) } // Static @@ -254,9 +260,9 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( }) // avoid conflict when clicking a toggler of an offcanvas, while another is open - const allReadyOpen = SelectorEngine.findOne(ACTIVE_SELECTOR) + const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR) if (allReadyOpen && allReadyOpen !== target) { - return + Offcanvas.getInstance(allReadyOpen).hide() } const data = Data.get(target, DATA_KEY) || new Offcanvas(target) |
