diff options
Diffstat (limited to 'js/dist/offcanvas.js')
-rw-r--r-- | js/dist/offcanvas.js | 275 |
1 files changed, 219 insertions, 56 deletions
diff --git a/js/dist/offcanvas.js b/js/dist/offcanvas.js index 20bdae46ec..3239497891 100644 --- a/js/dist/offcanvas.js +++ b/js/dist/offcanvas.js @@ -1,5 +1,5 @@ /*! - * Bootstrap offcanvas.js v5.0.0-beta3 (https://getbootstrap.com/) + * Bootstrap offcanvas.js v5.0.0 (https://getbootstrap.com/) * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ @@ -19,11 +19,12 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): util/index.js + * Bootstrap (v5.0.0): util/index.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const MILLISECONDS_MULTIPLIER = 1000; + const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp) const toType = obj => { if (obj === null || obj === undefined) { @@ -48,7 +49,7 @@ if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) { - hrefAttr = '#' + hrefAttr.split('#')[1]; + hrefAttr = `#${hrefAttr.split('#')[1]}`; } selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null; @@ -57,16 +58,6 @@ return selector; }; - const getSelectorFromElement = element => { - const selector = getSelector(element); - - if (selector) { - return document.querySelector(selector) ? selector : null; - } - - return null; - }; - const getElementFromSelector = element => { const selector = getSelector(element); return selector ? document.querySelector(selector) : null; @@ -95,8 +86,30 @@ return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; }; + const triggerTransitionEnd = element => { + element.dispatchEvent(new Event(TRANSITION_END)); + }; + const isElement = obj => (obj[0] || obj).nodeType; + const emulateTransitionEnd = (element, duration) => { + let called = false; + const durationPadding = 5; + const emulatedDuration = duration + durationPadding; + + function listener() { + called = true; + element.removeEventListener(TRANSITION_END, listener); + } + + element.addEventListener(TRANSITION_END, listener); + setTimeout(() => { + if (!called) { + triggerTransitionEnd(element); + } + }, emulatedDuration); + }; + const typeCheckConfig = (componentName, config, configTypes) => { Object.keys(configTypes).forEach(property => { const expectedTypes = configTypes[property]; @@ -104,7 +117,7 @@ const valueType = value && isElement(value) ? 'element' : toType(value); if (!new RegExp(expectedTypes).test(valueType)) { - throw new TypeError(`${componentName.toUpperCase()}: ` + `Option "${property}" provided type "${valueType}" ` + `but expected type "${expectedTypes}".`); + throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); } }); }; @@ -139,6 +152,8 @@ return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; }; + const reflow = element => element.offsetHeight; + const getjQuery = () => { const { jQuery @@ -177,13 +192,19 @@ }); }; + const execute = callback => { + if (typeof callback === 'function') { + callback(); + } + }; + /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): util/scrollBar.js + * Bootstrap (v5.0.0): util/scrollBar.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ - const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed'; + const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; const SELECTOR_STICKY_CONTENT = '.sticky-top'; const getWidth = () => { @@ -193,13 +214,25 @@ }; const hide = (width = getWidth()) => { - document.body.style.overflow = 'hidden'; + _disableOverFlow(); // give padding to element to balances the hidden scrollbar width + + + _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth + _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width); _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width); + }; - _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width); + const _disableOverFlow = () => { + const actualValue = document.body.style.overflow; + + if (actualValue) { + Manipulator__default['default'].setDataAttribute(document.body, 'overflow', actualValue); + } + + document.body.style.overflow = 'hidden'; }; const _setElementAttributes = (selector, styleProp, callback) => { @@ -212,25 +245,25 @@ const actualValue = element.style[styleProp]; const calculatedValue = window.getComputedStyle(element)[styleProp]; Manipulator__default['default'].setDataAttribute(element, styleProp, actualValue); - element.style[styleProp] = callback(Number.parseFloat(calculatedValue)) + 'px'; + element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`; }); }; const reset = () => { - document.body.style.overflow = 'auto'; + _resetElementAttributes('body', 'overflow'); + + _resetElementAttributes('body', 'paddingRight'); _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight'); _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight'); - - _resetElementAttributes('body', 'paddingRight'); }; const _resetElementAttributes = (selector, styleProp) => { SelectorEngine__default['default'].find(selector).forEach(element => { const value = Manipulator__default['default'].getDataAttribute(element, styleProp); - if (typeof value === 'undefined' && element === document.body) { + if (typeof value === 'undefined') { element.style.removeProperty(styleProp); } else { Manipulator__default['default'].removeDataAttribute(element, styleProp); @@ -241,7 +274,135 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): offcanvas.js + * Bootstrap (v5.0.0): util/backdrop.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + const Default$1 = { + 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 + clickCallback: null + }; + const DefaultType$1 = { + isVisible: 'boolean', + isAnimated: 'boolean', + rootElement: 'element', + clickCallback: '(function|null)' + }; + const NAME$1 = 'backdrop'; + const CLASS_NAME_BACKDROP = 'modal-backdrop'; + const CLASS_NAME_FADE = 'fade'; + const CLASS_NAME_SHOW$1 = 'show'; + const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$1}`; + + 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$1); + + this._emulateAnimation(() => { + execute(callback); + }); + } + + hide(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + + this._getElement().classList.remove(CLASS_NAME_SHOW$1); + + 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$1, + ...(typeof config === 'object' ? config : {}) + }; + typeCheckConfig(NAME$1, config, DefaultType$1); + return config; + } + + _append() { + if (this._isAppended) { + return; + } + + this._config.rootElement.appendChild(this._getElement()); + + EventHandler__default['default'].on(this._getElement(), EVENT_MOUSEDOWN, () => { + execute(this._config.clickCallback); + }); + this._isAppended = true; + } + + dispose() { + if (!this._isAppended) { + return; + } + + EventHandler__default['default'].off(this._element, EVENT_MOUSEDOWN); + + this._getElement().parentNode.removeChild(this._element); + + this._isAppended = false; + } + + _emulateAnimation(callback) { + if (!this._config.isAnimated) { + execute(callback); + return; + } + + const backdropTransitionDuration = getTransitionDurationFromElement(this._getElement()); + EventHandler__default['default'].one(this._getElement(), 'transitionend', () => execute(callback)); + emulateTransitionEnd(this._getElement(), backdropTransitionDuration); + } + + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v5.0.0): offcanvas.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -267,11 +428,8 @@ keyboard: 'boolean', 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}`; const EVENT_HIDE = `hide${EVENT_KEY}`; @@ -279,6 +437,7 @@ 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"]'; /** @@ -292,6 +451,7 @@ super(element); this._config = this._getConfig(config); this._isShown = false; + this._backdrop = this._initializeBackDrop(); this._addEventListeners(); } // Getters @@ -326,15 +486,13 @@ 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) { hide(); - } - this._element.classList.add(CLASS_NAME_TOGGLING); + this._enforceFocusOnElement(this._element); + } this._element.removeAttribute('aria-hidden'); @@ -345,16 +503,14 @@ this._element.classList.add(CLASS_NAME_SHOW); const completeCallBack = () => { - this._element.classList.remove(CLASS_NAME_TOGGLING); - EventHandler__default['default'].trigger(this._element, EVENT_SHOWN, { relatedTarget }); - - this._enforceFocusOnElement(this._element); }; - setTimeout(completeCallBack, getTransitionDurationFromElement(this._element)); + const transitionDuration = getTransitionDurationFromElement(this._element); + EventHandler__default['default'].one(this._element, 'transitionend', completeCallBack); + emulateTransitionEnd(this._element, transitionDuration); } hide() { @@ -368,8 +524,6 @@ return; } - this._element.classList.add(CLASS_NAME_TOGGLING); - EventHandler__default['default'].off(document, EVENT_FOCUSIN); this._element.blur(); @@ -378,6 +532,8 @@ this._element.classList.remove(CLASS_NAME_SHOW); + this._backdrop.hide(); + const completeCallback = () => { this._element.setAttribute('aria-hidden', true); @@ -387,20 +543,25 @@ this._element.style.visibility = 'hidden'; - if (this._config.backdrop) { - document.body.classList.remove(CLASS_NAME_BACKDROP_BODY); - } - if (!this._config.scroll) { reset(); } EventHandler__default['default'].trigger(this._element, EVENT_HIDDEN); - - this._element.classList.remove(CLASS_NAME_TOGGLING); }; - setTimeout(completeCallback, getTransitionDurationFromElement(this._element)); + const transitionDuration = getTransitionDurationFromElement(this._element); + EventHandler__default['default'].one(this._element, 'transitionend', completeCallback); + emulateTransitionEnd(this._element, transitionDuration); + } + + dispose() { + this._backdrop.dispose(); + + super.dispose(); + EventHandler__default['default'].off(document, EVENT_FOCUSIN); + this._config = null; + this._backdrop = null; } // Private @@ -413,6 +574,15 @@ return config; } + _initializeBackDrop() { + return new Backdrop({ + isVisible: this._config.backdrop, + isAnimated: true, + rootElement: this._element.parentNode, + clickCallback: () => this.hide() + }); + } + _enforceFocusOnElement(element) { EventHandler__default['default'].off(document, EVENT_FOCUSIN); // guard against infinite focus loop @@ -426,18 +596,11 @@ _addEventListeners() { EventHandler__default['default'].on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide()); - EventHandler__default['default'].on(document, 'keydown', event => { + EventHandler__default['default'].on(this._element, EVENT_KEYDOWN_DISMISS, event => { if (this._config.keyboard && event.key === ESCAPE_KEY) { this.hide(); } }); - EventHandler__default['default'].on(document, EVENT_CLICK_DATA_API, event => { - const target = SelectorEngine__default['default'].findOne(getSelectorFromElement(event.target)); - - if (!this._element.contains(event.target) && target !== this._element) { - this.hide(); - } - }); } // Static @@ -483,10 +646,10 @@ } }); // avoid conflict when clicking a toggler of an offcanvas, while another is open - const allReadyOpen = SelectorEngine__default['default'].findOne(ACTIVE_SELECTOR); + const allReadyOpen = SelectorEngine__default['default'].findOne(OPEN_SELECTOR); if (allReadyOpen && allReadyOpen !== target) { - return; + Offcanvas.getInstance(allReadyOpen).hide(); } const data = Data__default['default'].get(target, DATA_KEY) || new Offcanvas(target); |