diff options
Diffstat (limited to 'dist/js/bootstrap.esm.js')
-rw-r--r-- | dist/js/bootstrap.esm.js | 966 |
1 files changed, 508 insertions, 458 deletions
diff --git a/dist/js/bootstrap.esm.js b/dist/js/bootstrap.esm.js index 3ca4ebc8f1..47e5a5430e 100644 --- a/dist/js/bootstrap.esm.js +++ b/dist/js/bootstrap.esm.js @@ -1,5 +1,5 @@ /*! - * Bootstrap v5.0.0-beta3 (https://getbootstrap.com/) + * Bootstrap 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) */ @@ -7,7 +7,7 @@ import * as Popper from '@popperjs/core'; /** * -------------------------------------------------------------------------- - * 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) * -------------------------------------------------------------------------- */ @@ -52,7 +52,7 @@ const getSelector = element => { if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) { - hrefAttr = '#' + hrefAttr.split('#')[1]; + hrefAttr = `#${hrefAttr.split('#')[1]}`; } selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null; @@ -130,7 +130,7 @@ const typeCheckConfig = (componentName, config, configTypes) => { 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}".`); } }); }; @@ -188,7 +188,7 @@ const findShadowRoot = element => { return findShadowRoot(element.parentNode); }; -const noop = () => function () {}; +const noop = () => {}; const reflow = element => element.offsetHeight; @@ -232,9 +232,15 @@ const defineJQueryPlugin = (name, plugin) => { }); }; +const execute = callback => { + if (typeof callback === 'function') { + callback(); + } +}; + /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): dom/data.js + * Bootstrap (v5.0.0): dom/data.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -288,7 +294,7 @@ var Data = { /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): dom/event-handler.js + * Bootstrap (v5.0.0): dom/event-handler.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -308,6 +314,7 @@ const customEvents = { mouseenter: 'mouseover', mouseleave: 'mouseout' }; +const customEventsRegex = /^(mouseenter|mouseleave)/i; const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); /** * ------------------------------------------------------------------------ @@ -351,7 +358,7 @@ function bootstrapDelegationHandler(element, selector, fn) { if (handler.oneOff) { // eslint-disable-next-line unicorn/consistent-destructuring - EventHandler.off(element, event.type, fn); + EventHandler.off(element, event.type, selector, fn); } return fn.apply(target, [event]); @@ -380,15 +387,8 @@ function findHandler(events, handler, delegationSelector = null) { function normalizeParams(originalTypeEvent, handler, delegationFn) { const delegation = typeof handler === 'string'; - const originalHandler = delegation ? delegationFn : handler; // allow to get the native events from namespaced events ('click.bs.button' --> 'click') - - let typeEvent = originalTypeEvent.replace(stripNameRegex, ''); - const custom = customEvents[typeEvent]; - - if (custom) { - typeEvent = custom; - } - + const originalHandler = delegation ? delegationFn : handler; + let typeEvent = getTypeEvent(originalTypeEvent); const isNative = nativeEvents.has(typeEvent); if (!isNative) { @@ -406,6 +406,24 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) { if (!handler) { handler = delegationFn; delegationFn = null; + } // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position + // this prevents the handler from being dispatched the same way as mouseover or mouseout does + + + if (customEventsRegex.test(originalTypeEvent)) { + const wrapFn = fn => { + return function (event) { + if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { + return fn.call(this, event); + } + }; + }; + + if (delegationFn) { + delegationFn = wrapFn(delegationFn); + } else { + handler = wrapFn(handler); + } } const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn); @@ -449,6 +467,12 @@ function removeNamespacedHandlers(element, events, typeEvent, namespace) { }); } +function getTypeEvent(event) { + // allow to get the native events from namespaced events ('click.bs.button' --> 'click') + event = event.replace(stripNameRegex, ''); + return customEvents[event] || event; +} + const EventHandler = { on(element, event, handler, delegationFn) { addHandler(element, event, handler, delegationFn, false); @@ -501,7 +525,7 @@ const EventHandler = { } const $ = getjQuery(); - const typeEvent = event.replace(stripNameRegex, ''); + const typeEvent = getTypeEvent(event); const inNamespace = event !== typeEvent; const isNative = nativeEvents.has(typeEvent); let jQueryEvent; @@ -559,7 +583,7 @@ const EventHandler = { /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): base-component.js + * Bootstrap (v5.0.0): base-component.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -569,7 +593,7 @@ const EventHandler = { * ------------------------------------------------------------------------ */ -const VERSION = '5.0.0-beta3'; +const VERSION = '5.0.0'; class BaseComponent { constructor(element) { @@ -585,6 +609,7 @@ class BaseComponent { dispose() { Data.remove(this._element, this.constructor.DATA_KEY); + EventHandler.off(this._element, `.${this.constructor.DATA_KEY}`); this._element = null; } /** Static */ @@ -602,7 +627,7 @@ class BaseComponent { /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): alert.js + * Bootstrap (v5.0.0): alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -612,7 +637,7 @@ class BaseComponent { * ------------------------------------------------------------------------ */ -const NAME$b = 'alert'; +const NAME$c = 'alert'; const DATA_KEY$b = 'bs.alert'; const EVENT_KEY$b = `.${DATA_KEY$b}`; const DATA_API_KEY$8 = '.data-api'; @@ -621,8 +646,8 @@ const EVENT_CLOSE = `close${EVENT_KEY$b}`; const EVENT_CLOSED = `closed${EVENT_KEY$b}`; const EVENT_CLICK_DATA_API$7 = `click${EVENT_KEY$b}${DATA_API_KEY$8}`; const CLASS_NAME_ALERT = 'alert'; -const CLASS_NAME_FADE$5 = 'fade'; -const CLASS_NAME_SHOW$8 = 'show'; +const CLASS_NAME_FADE$6 = 'fade'; +const CLASS_NAME_SHOW$9 = 'show'; /** * ------------------------------------------------------------------------ * Class Definition @@ -658,9 +683,9 @@ class Alert extends BaseComponent { } _removeElement(element) { - element.classList.remove(CLASS_NAME_SHOW$8); + element.classList.remove(CLASS_NAME_SHOW$9); - if (!element.classList.contains(CLASS_NAME_FADE$5)) { + if (!element.classList.contains(CLASS_NAME_FADE$6)) { this._destroyElement(element); return; @@ -720,11 +745,11 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$7, SELECTOR_DISMISS, Alert.handle * add .Alert to jQuery only if jQuery is present */ -defineJQueryPlugin(NAME$b, Alert); +defineJQueryPlugin(NAME$c, Alert); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): button.js + * Bootstrap (v5.0.0): button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -734,7 +759,7 @@ defineJQueryPlugin(NAME$b, Alert); * ------------------------------------------------------------------------ */ -const NAME$a = 'button'; +const NAME$b = 'button'; const DATA_KEY$a = 'bs.button'; const EVENT_KEY$a = `.${DATA_KEY$a}`; const DATA_API_KEY$7 = '.data-api'; @@ -800,11 +825,11 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event * add .Button to jQuery only if jQuery is present */ -defineJQueryPlugin(NAME$a, Button); +defineJQueryPlugin(NAME$b, Button); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): dom/manipulator.js + * Bootstrap (v5.0.0): dom/manipulator.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -878,7 +903,7 @@ const Manipulator = { /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): dom/selector-engine.js + * Bootstrap (v5.0.0): dom/selector-engine.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -949,7 +974,7 @@ const SelectorEngine = { /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): carousel.js + * Bootstrap (v5.0.0): carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -959,7 +984,7 @@ const SelectorEngine = { * ------------------------------------------------------------------------ */ -const NAME$9 = 'carousel'; +const NAME$a = 'carousel'; const DATA_KEY$9 = 'bs.carousel'; const EVENT_KEY$9 = `.${DATA_KEY$9}`; const DATA_API_KEY$6 = '.data-api'; @@ -968,7 +993,7 @@ const ARROW_RIGHT_KEY = 'ArrowRight'; const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch const SWIPE_THRESHOLD = 40; -const Default$8 = { +const Default$9 = { interval: 5000, keyboard: true, slide: false, @@ -976,7 +1001,7 @@ const Default$8 = { wrap: true, touch: true }; -const DefaultType$8 = { +const DefaultType$9 = { interval: '(number|boolean)', keyboard: 'boolean', slide: '(boolean|string)', @@ -1047,7 +1072,7 @@ class Carousel extends BaseComponent { static get Default() { - return Default$8; + return Default$9; } static get DATA_KEY() { @@ -1132,7 +1157,6 @@ class Carousel extends BaseComponent { } dispose() { - EventHandler.off(this._element, EVENT_KEY$9); this._items = null; this._config = null; this._interval = null; @@ -1145,10 +1169,10 @@ class Carousel extends BaseComponent { _getConfig(config) { - config = { ...Default$8, + config = { ...Default$9, ...config }; - typeCheckConfig(NAME$9, config, DefaultType$8); + typeCheckConfig(NAME$a, config, DefaultType$9); return config; } @@ -1247,11 +1271,11 @@ class Carousel extends BaseComponent { if (event.key === ARROW_LEFT_KEY) { event.preventDefault(); - this._slide(DIRECTION_LEFT); + this._slide(DIRECTION_RIGHT); } else if (event.key === ARROW_RIGHT_KEY) { event.preventDefault(); - this._slide(DIRECTION_RIGHT); + this._slide(DIRECTION_LEFT); } } @@ -1413,10 +1437,10 @@ class Carousel extends BaseComponent { } if (isRTL()) { - return direction === DIRECTION_RIGHT ? ORDER_PREV : ORDER_NEXT; + return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; } - return direction === DIRECTION_RIGHT ? ORDER_NEXT : ORDER_PREV; + return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; } _orderToDirection(order) { @@ -1425,16 +1449,16 @@ class Carousel extends BaseComponent { } if (isRTL()) { - return order === ORDER_NEXT ? DIRECTION_LEFT : DIRECTION_RIGHT; + return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT; } - return order === ORDER_NEXT ? DIRECTION_RIGHT : DIRECTION_LEFT; + return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT; } // Static static carouselInterface(element, config) { let data = Data.get(element, DATA_KEY$9); - let _config = { ...Default$8, + let _config = { ...Default$9, ...Manipulator.getDataAttributes(element) }; @@ -1518,11 +1542,11 @@ EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => { * add .Carousel to jQuery only if jQuery is present */ -defineJQueryPlugin(NAME$9, Carousel); +defineJQueryPlugin(NAME$a, Carousel); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): collapse.js + * Bootstrap (v5.0.0): collapse.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -1532,15 +1556,15 @@ defineJQueryPlugin(NAME$9, Carousel); * ------------------------------------------------------------------------ */ -const NAME$8 = 'collapse'; +const NAME$9 = 'collapse'; const DATA_KEY$8 = 'bs.collapse'; const EVENT_KEY$8 = `.${DATA_KEY$8}`; const DATA_API_KEY$5 = '.data-api'; -const Default$7 = { +const Default$8 = { toggle: true, parent: '' }; -const DefaultType$7 = { +const DefaultType$8 = { toggle: 'boolean', parent: '(string|element)' }; @@ -1549,7 +1573,7 @@ const EVENT_SHOWN$5 = `shown${EVENT_KEY$8}`; const EVENT_HIDE$5 = `hide${EVENT_KEY$8}`; const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$8}`; const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; -const CLASS_NAME_SHOW$7 = 'show'; +const CLASS_NAME_SHOW$8 = 'show'; const CLASS_NAME_COLLAPSE = 'collapse'; const CLASS_NAME_COLLAPSING = 'collapsing'; const CLASS_NAME_COLLAPSED = 'collapsed'; @@ -1596,7 +1620,7 @@ class Collapse extends BaseComponent { static get Default() { - return Default$7; + return Default$8; } static get DATA_KEY() { @@ -1605,7 +1629,7 @@ class Collapse extends BaseComponent { toggle() { - if (this._element.classList.contains(CLASS_NAME_SHOW$7)) { + if (this._element.classList.contains(CLASS_NAME_SHOW$8)) { this.hide(); } else { this.show(); @@ -1613,7 +1637,7 @@ class Collapse extends BaseComponent { } show() { - if (this._isTransitioning || this._element.classList.contains(CLASS_NAME_SHOW$7)) { + if (this._isTransitioning || this._element.classList.contains(CLASS_NAME_SHOW$8)) { return; } @@ -1683,7 +1707,7 @@ class Collapse extends BaseComponent { const complete = () => { this._element.classList.remove(CLASS_NAME_COLLAPSING); - this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$8); this._element.style[dimension] = ''; this.setTransitioning(false); @@ -1699,7 +1723,7 @@ class Collapse extends BaseComponent { } hide() { - if (this._isTransitioning || !this._element.classList.contains(CLASS_NAME_SHOW$7)) { + if (this._isTransitioning || !this._element.classList.contains(CLASS_NAME_SHOW$8)) { return; } @@ -1716,7 +1740,7 @@ class Collapse extends BaseComponent { this._element.classList.add(CLASS_NAME_COLLAPSING); - this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$8); const triggerArrayLength = this._triggerArray.length; @@ -1725,7 +1749,7 @@ class Collapse extends BaseComponent { const trigger = this._triggerArray[i]; const elem = getElementFromSelector(trigger); - if (elem && !elem.classList.contains(CLASS_NAME_SHOW$7)) { + if (elem && !elem.classList.contains(CLASS_NAME_SHOW$8)) { trigger.classList.add(CLASS_NAME_COLLAPSED); trigger.setAttribute('aria-expanded', false); } @@ -1764,12 +1788,12 @@ class Collapse extends BaseComponent { _getConfig(config) { - config = { ...Default$7, + config = { ...Default$8, ...config }; config.toggle = Boolean(config.toggle); // Coerce string values - typeCheckConfig(NAME$8, config, DefaultType$7); + typeCheckConfig(NAME$9, config, DefaultType$8); return config; } @@ -1805,7 +1829,7 @@ class Collapse extends BaseComponent { return; } - const isOpen = element.classList.contains(CLASS_NAME_SHOW$7); + const isOpen = element.classList.contains(CLASS_NAME_SHOW$8); triggerArray.forEach(elem => { if (isOpen) { elem.classList.remove(CLASS_NAME_COLLAPSED); @@ -1820,7 +1844,7 @@ class Collapse extends BaseComponent { static collapseInterface(element, config) { let data = Data.get(element, DATA_KEY$8); - const _config = { ...Default$7, + const _config = { ...Default$8, ...Manipulator.getDataAttributes(element), ...(typeof config === 'object' && config ? config : {}) }; @@ -1891,11 +1915,11 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, functi * add .Collapse to jQuery only if jQuery is present */ -defineJQueryPlugin(NAME$8, Collapse); +defineJQueryPlugin(NAME$9, Collapse); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): dropdown.js + * Bootstrap (v5.0.0): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -1905,7 +1929,7 @@ defineJQueryPlugin(NAME$8, Collapse); * ------------------------------------------------------------------------ */ -const NAME$7 = 'dropdown'; +const NAME$8 = 'dropdown'; const DATA_KEY$7 = 'bs.dropdown'; const EVENT_KEY$7 = `.${DATA_KEY$7}`; const DATA_API_KEY$4 = '.data-api'; @@ -1925,8 +1949,7 @@ const EVENT_CLICK = `click${EVENT_KEY$7}`; const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$7}${DATA_API_KEY$4}`; const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$7}${DATA_API_KEY$4}`; -const CLASS_NAME_DISABLED = 'disabled'; -const CLASS_NAME_SHOW$6 = 'show'; +const CLASS_NAME_SHOW$7 = 'show'; const CLASS_NAME_DROPUP = 'dropup'; const CLASS_NAME_DROPEND = 'dropend'; const CLASS_NAME_DROPSTART = 'dropstart'; @@ -1941,19 +1964,21 @@ const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; -const Default$6 = { +const Default$7 = { offset: [0, 2], boundary: 'clippingParents', reference: 'toggle', display: 'dynamic', - popperConfig: null + popperConfig: null, + autoClose: true }; -const DefaultType$6 = { +const DefaultType$7 = { offset: '(array|string|function)', boundary: '(string|element)', reference: '(string|element|object)', display: 'string', - popperConfig: '(null|object|function)' + popperConfig: '(null|object|function)', + autoClose: '(boolean|string)' }; /** * ------------------------------------------------------------------------ @@ -1974,11 +1999,11 @@ class Dropdown extends BaseComponent { static get Default() { - return Default$6; + return Default$7; } static get DefaultType() { - return DefaultType$6; + return DefaultType$7; } static get DATA_KEY() { @@ -1987,15 +2012,14 @@ class Dropdown extends BaseComponent { toggle() { - if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED)) { + if (isDisabled(this._element)) { return; } - const isActive = this._element.classList.contains(CLASS_NAME_SHOW$6); - - Dropdown.clearMenus(); + const isActive = this._element.classList.contains(CLASS_NAME_SHOW$7); if (isActive) { + this.hide(); return; } @@ -2003,7 +2027,7 @@ class Dropdown extends BaseComponent { } show() { - if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED) || this._menu.classList.contains(CLASS_NAME_SHOW$6)) { + if (isDisabled(this._element) || this._menu.classList.contains(CLASS_NAME_SHOW$7)) { return; } @@ -2054,48 +2078,33 @@ class Dropdown extends BaseComponent { if ('ontouchstart' in document.documentElement && !parent.closest(SELECTOR_NAVBAR_NAV)) { - [].concat(...document.body.children).forEach(elem => EventHandler.on(elem, 'mouseover', null, noop())); + [].concat(...document.body.children).forEach(elem => EventHandler.on(elem, 'mouseover', noop)); } this._element.focus(); this._element.setAttribute('aria-expanded', true); - this._menu.classList.toggle(CLASS_NAME_SHOW$6); + this._menu.classList.toggle(CLASS_NAME_SHOW$7); - this._element.classList.toggle(CLASS_NAME_SHOW$6); + this._element.classList.toggle(CLASS_NAME_SHOW$7); EventHandler.trigger(this._element, EVENT_SHOWN$4, relatedTarget); } hide() { - if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED) || !this._menu.classList.contains(CLASS_NAME_SHOW$6)) { + if (isDisabled(this._element) || !this._menu.classList.contains(CLASS_NAME_SHOW$7)) { return; } const relatedTarget = { relatedTarget: this._element }; - const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4, relatedTarget); - - if (hideEvent.defaultPrevented) { - return; - } - - if (this._popper) { - this._popper.destroy(); - } - - this._menu.classList.toggle(CLASS_NAME_SHOW$6); - - this._element.classList.toggle(CLASS_NAME_SHOW$6); - Manipulator.removeDataAttribute(this._menu, 'popper'); - EventHandler.trigger(this._element, EVENT_HIDDEN$4, relatedTarget); + this._completeHide(relatedTarget); } dispose() { - EventHandler.off(this._element, EVENT_KEY$7); this._menu = null; if (this._popper) { @@ -2123,16 +2132,43 @@ class Dropdown extends BaseComponent { }); } + _completeHide(relatedTarget) { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4, relatedTarget); + + if (hideEvent.defaultPrevented) { + return; + } // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + + if ('ontouchstart' in document.documentElement) { + [].concat(...document.body.children).forEach(elem => EventHandler.off(elem, 'mouseover', noop)); + } + + if (this._popper) { + this._popper.destroy(); + } + + this._menu.classList.remove(CLASS_NAME_SHOW$7); + + this._element.classList.remove(CLASS_NAME_SHOW$7); + + this._element.setAttribute('aria-expanded', 'false'); + + Manipulator.removeDataAttribute(this._menu, 'popper'); + EventHandler.trigger(this._element, EVENT_HIDDEN$4, relatedTarget); + } + _getConfig(config) { config = { ...this.constructor.Default, ...Manipulator.getDataAttributes(this._element), ...config }; - typeCheckConfig(NAME$7, config, this.constructor.DefaultType); + typeCheckConfig(NAME$8, config, this.constructor.DefaultType); if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { // Popper virtual elements require a getBoundingClientRect method - throw new TypeError(`${NAME$7.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); + throw new TypeError(`${NAME$8.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); } return config; @@ -2209,6 +2245,29 @@ class Dropdown extends BaseComponent { return { ...defaultBsPopperConfig, ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig) }; + } + + _selectMenuItem(event) { + const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible); + + if (!items.length) { + return; + } + + let index = items.indexOf(event.target); // Up + + if (event.key === ARROW_UP_KEY && index > 0) { + index--; + } // Down + + + if (event.key === ARROW_DOWN_KEY && index < items.length - 1) { + index++; + } // index is -1 if the first keydown is an ArrowUp + + + index = index === -1 ? 0 : index; + items[index].focus(); } // Static @@ -2242,7 +2301,7 @@ class Dropdown extends BaseComponent { return; } - if (/input|select|textarea|form/i.test(event.target.tagName)) { + if (/input|select|option|textarea|form/i.test(event.target.tagName)) { return; } } @@ -2251,58 +2310,38 @@ class Dropdown extends BaseComponent { for (let i = 0, len = toggles.length; i < len; i++) { const context = Data.get(toggles[i], DATA_KEY$7); - const relatedTarget = { - relatedTarget: toggles[i] - }; - if (event && event.type === 'click') { - relatedTarget.clickEvent = event; - } - - if (!context) { + if (!context || context._config.autoClose === false) { continue; } - const dropdownMenu = context._menu; - - if (!toggles[i].classList.contains(CLASS_NAME_SHOW$6)) { + if (!context._element.classList.contains(CLASS_NAME_SHOW$7)) { continue; } + const relatedTarget = { + relatedTarget: context._element + }; + if (event) { - // Don't close the menu if the clicked element or one of its parents is the dropdown button - if ([context._element].some(element => event.composedPath().includes(element))) { + const composedPath = event.composedPath(); + const isMenuTarget = composedPath.includes(context._menu); + + if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { continue; } // Tab navigation through the dropdown menu shouldn't close the menu - if (event.type === 'keyup' && event.key === TAB_KEY && dropdownMenu.contains(event.target)) { + if (event.type === 'keyup' && event.key === TAB_KEY && context._menu.contains(event.target)) { continue; } - } - - const hideEvent = EventHandler.trigger(toggles[i], EVENT_HIDE$4, relatedTarget); - - if (hideEvent.defaultPrevented) { - continue; - } // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - - - if ('ontouchstart' in document.documentElement) { - [].concat(...document.body.children).forEach(elem => EventHandler.off(elem, 'mouseover', null, noop())); - } - - toggles[i].setAttribute('aria-expanded', 'false'); - if (context._popper) { - context._popper.destroy(); + if (event.type === 'click') { + relatedTarget.clickEvent = event; + } } - dropdownMenu.classList.remove(CLASS_NAME_SHOW$6); - toggles[i].classList.remove(CLASS_NAME_SHOW$6); - Manipulator.removeDataAttribute(dropdownMenu, 'popper'); - EventHandler.trigger(toggles[i], EVENT_HIDDEN$4, relatedTarget); + context._completeHide(relatedTarget); } } @@ -2322,26 +2361,29 @@ class Dropdown extends BaseComponent { return; } + const isActive = this.classList.contains(CLASS_NAME_SHOW$7); + + if (!isActive && event.key === ESCAPE_KEY$2) { + return; + } + event.preventDefault(); event.stopPropagation(); - if (this.disabled || this.classList.contains(CLASS_NAME_DISABLED)) { + if (isDisabled(this)) { return; } - const parent = Dropdown.getParentFromElement(this); - const isActive = this.classList.contains(CLASS_NAME_SHOW$6); + const getToggleButton = () => this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0]; if (event.key === ESCAPE_KEY$2) { - const button = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0]; - button.focus(); + getToggleButton().focus(); Dropdown.clearMenus(); return; } if (!isActive && (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY)) { - const button = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0]; - button.click(); + getToggleButton().click(); return; } @@ -2350,26 +2392,7 @@ class Dropdown extends BaseComponent { return; } - const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, parent).filter(isVisible); - - if (!items.length) { - return; - } - - let index = items.indexOf(event.target); // Up - - if (event.key === ARROW_UP_KEY && index > 0) { - index--; - } // Down - - - if (event.key === ARROW_DOWN_KEY && index < items.length - 1) { - index++; - } // index is -1 if the first keydown is an ArrowUp - - - index = index === -1 ? 0 : index; - items[index].focus(); + Dropdown.getInstance(getToggleButton())._selectMenuItem(event); } } @@ -2395,11 +2418,213 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, functi * add .Dropdown to jQuery only if jQuery is present */ -defineJQueryPlugin(NAME$7, Dropdown); +defineJQueryPlugin(NAME$8, Dropdown); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): modal.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, .sticky-top'; +const SELECTOR_STICKY_CONTENT = '.sticky-top'; + +const getWidth = () => { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth; + return Math.abs(window.innerWidth - documentWidth); +}; + +const hide = (width = getWidth()) => { + _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); +}; + +const _disableOverFlow = () => { + const actualValue = document.body.style.overflow; + + if (actualValue) { + Manipulator.setDataAttribute(document.body, 'overflow', actualValue); + } + + document.body.style.overflow = 'hidden'; +}; + +const _setElementAttributes = (selector, styleProp, callback) => { + const scrollbarWidth = getWidth(); + SelectorEngine.find(selector).forEach(element => { + if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) { + return; + } + + const actualValue = element.style[styleProp]; + const calculatedValue = window.getComputedStyle(element)[styleProp]; + Manipulator.setDataAttribute(element, styleProp, actualValue); + element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`; + }); +}; + +const reset = () => { + _resetElementAttributes('body', 'overflow'); + + _resetElementAttributes('body', 'paddingRight'); + + _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight'); + + _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight'); +}; + +const _resetElementAttributes = (selector, styleProp) => { + SelectorEngine.find(selector).forEach(element => { + const value = Manipulator.getDataAttribute(element, styleProp); + + if (typeof value === 'undefined') { + element.style.removeProperty(styleProp); + } else { + Manipulator.removeDataAttribute(element, styleProp); + element.style[styleProp] = value; + } + }); +}; + +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.0.0): util/backdrop.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ +const Default$6 = { + 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$6 = { + isVisible: 'boolean', + isAnimated: 'boolean', + rootElement: 'element', + clickCallback: '(function|null)' +}; +const NAME$7 = 'backdrop'; +const CLASS_NAME_BACKDROP = 'modal-backdrop'; +const CLASS_NAME_FADE$5 = 'fade'; +const CLASS_NAME_SHOW$6 = 'show'; +const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$7}`; + +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$6); + + this._emulateAnimation(() => { + execute(callback); + }); + } + + hide(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + + this._getElement().classList.remove(CLASS_NAME_SHOW$6); + + 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$5); + } + + this._element = backdrop; + } + + return this._element; + } + + _getConfig(config) { + config = { ...Default$6, + ...(typeof config === 'object' ? config : {}) + }; + typeCheckConfig(NAME$7, config, DefaultType$6); + return config; + } + + _append() { + if (this._isAppended) { + return; + } + + this._config.rootElement.appendChild(this._getElement()); + + EventHandler.on(this._getElement(), EVENT_MOUSEDOWN, () => { + execute(this._config.clickCallback); + }); + this._isAppended = true; + } + + dispose() { + if (!this._isAppended) { + return; + } + + EventHandler.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.one(this._getElement(), 'transitionend', () => execute(callback)); + emulateTransitionEnd(this._getElement(), backdropTransitionDuration); + } + +} + +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.0.0): modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -2432,12 +2657,10 @@ const EVENT_SHOWN$3 = `shown${EVENT_KEY$6}`; const EVENT_FOCUSIN$1 = `focusin${EVENT_KEY$6}`; const EVENT_RESIZE = `resize${EVENT_KEY$6}`; const EVENT_CLICK_DISMISS$2 = `click.dismiss${EVENT_KEY$6}`; -const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$6}`; +const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$6}`; const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY$6}`; const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$6}`; const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; -const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'; -const CLASS_NAME_BACKDROP = 'modal-backdrop'; const CLASS_NAME_OPEN = 'modal-open'; const CLASS_NAME_FADE$4 = 'fade'; const CLASS_NAME_SHOW$5 = 'show'; @@ -2446,8 +2669,6 @@ const SELECTOR_DIALOG = '.modal-dialog'; const SELECTOR_MODAL_BODY = '.modal-body'; const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; const SELECTOR_DATA_DISMISS$2 = '[data-bs-dismiss="modal"]'; -const SELECTOR_FIXED_CONTENT$1 = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; -const SELECTOR_STICKY_CONTENT$1 = '.sticky-top'; /** * ------------------------------------------------------------------------ * Class Definition @@ -2459,12 +2680,10 @@ class Modal extends BaseComponent { super(element); this._config = this._getConfig(config); this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); - this._backdrop = null; + this._backdrop = this._initializeBackDrop(); this._isShown = false; - this._isBodyOverflowing = false; this._ignoreBackdropClick = false; this._isTransitioning = false; - this._scrollbarWidth = 0; } // Getters @@ -2499,10 +2718,8 @@ class Modal extends BaseComponent { } this._isShown = true; - - this._checkScrollbar(); - - this._setScrollbar(); + hide(); + document.body.classList.add(CLASS_NAME_OPEN); this._adjustDialog(); @@ -2566,7 +2783,7 @@ class Modal extends BaseComponent { } dispose() { - [window, this._element, this._dialog].forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY$6)); + [window, this._dialog].forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY$6)); super.dispose(); /** * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API` @@ -2577,12 +2794,13 @@ class Modal extends BaseComponent { EventHandler.off(document, EVENT_FOCUSIN$1); this._config = null; this._dialog = null; + + this._backdrop.dispose(); + this._backdrop = null; this._isShown = null; - this._isBodyOverflowing = null; this._ignoreBackdropClick = null; this._isTransitioning = null; - this._scrollbarWidth = null; } handleUpdate() { @@ -2590,8 +2808,17 @@ 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$5, + ...Manipulator.getDataAttributes(this._element), ...config }; typeCheckConfig(NAME$6, config, DefaultType$5); @@ -2664,7 +2891,7 @@ class Modal extends BaseComponent { _setEscapeEvent() { if (this._isShown) { - EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => { if (this._config.keyboard && event.key === ESCAPE_KEY$1) { event.preventDefault(); this.hide(); @@ -2673,7 +2900,7 @@ class Modal extends BaseComponent { } }); } else { - EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS); + EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS$1); } } @@ -2696,85 +2923,35 @@ class Modal extends BaseComponent { this._isTransitioning = false; - this._showBackdrop(() => { + this._backdrop.hide(() => { document.body.classList.remove(CLASS_NAME_OPEN); this._resetAdjustments(); - this._resetScrollbar(); - + reset(); EventHandler.trigger(this._element, EVENT_HIDDEN$3); }); } - _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$4); - } - - document.body.appendChild(this._backdrop); - EventHandler.on(this._element, EVENT_CLICK_DISMISS$2, 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$2, event => { + if (this._ignoreBackdropClick) { + this._ignoreBackdropClick = false; + return; } - this._backdrop.classList.add(CLASS_NAME_SHOW$5); - - 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$5); - - const callbackRemove = () => { - this._removeBackdrop(); - - callback(); - }; - - if (isAnimated) { - const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop); - EventHandler.one(this._backdrop, 'transitionend', callbackRemove); - emulateTransitionEnd(this._backdrop, backdropTransitionDuration); - } else { - callbackRemove(); + if (this._config.backdrop === true) { + this.hide(); + } else if (this._config.backdrop === 'static') { + this._triggerBackdropTransition(); } - } else { - callback(); - } + }); + + this._backdrop.show(callback); } _isAnimated() { @@ -2818,103 +2995,37 @@ class Modal extends BaseComponent { _adjustDialog() { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const scrollbarWidth = getWidth(); + const isBodyOverflowing = scrollbarWidth > 0; - if (!this._isBodyOverflowing && isModalOverflowing && !isRTL() || this._isBodyOverflowing && !isModalOverflowing && isRTL()) { - this._element.style.paddingLeft = `${this._scrollbarWidth}px`; + if (!isBodyOverflowing && isModalOverflowing && !isRTL() || isBodyOverflowing && !isModalOverflowing && isRTL()) { + this._element.style.paddingLeft = `${scrollbarWidth}px`; } - if (this._isBodyOverflowing && !isModalOverflowing && !isRTL() || !this._isBodyOverflowing && isModalOverflowing && isRTL()) { - this._element.style.paddingRight = `${this._scrollbarWidth}px`; + if (isBodyOverflowing && !isModalOverflowing && !isRTL() || !isBodyOverflowing && isModalOverflowing && isRTL()) { + this._element.style.paddingRight = `${scrollbarWidth}px`; } } _resetAdjustments() { this._element.style.paddingLeft = ''; this._element.style.paddingRight = ''; - } - - _checkScrollbar() { - const rect = document.body.getBoundingClientRect(); - this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth; - this._scrollbarWidth = this._getScrollbarWidth(); - } - - _setScrollbar() { - if (this._isBodyOverflowing) { - this._setElementAttributes(SELECTOR_FIXED_CONTENT$1, 'paddingRight', calculatedValue => calculatedValue + this._scrollbarWidth); - - this._setElementAttributes(SELECTOR_STICKY_CONTENT$1, 'marginRight', calculatedValue => calculatedValue - this._scrollbarWidth); - - this._setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + this._scrollbarWidth); - } - - document.body.classList.add(CLASS_NAME_OPEN); - } - - _setElementAttributes(selector, styleProp, callback) { - SelectorEngine.find(selector).forEach(element => { - if (element !== document.body && window.innerWidth > element.clientWidth + this._scrollbarWidth) { - return; - } - - const actualValue = element.style[styleProp]; - const calculatedValue = window.getComputedStyle(element)[styleProp]; - Manipulator.setDataAttribute(element, styleProp, actualValue); - element.style[styleProp] = callback(Number.parseFloat(calculatedValue)) + 'px'; - }); - } - - _resetScrollbar() { - this._resetElementAttributes(SELECTOR_FIXED_CONTENT$1, 'paddingRight'); - - this._resetElementAttributes(SELECTOR_STICKY_CONTENT$1, 'marginRight'); - - this._resetElementAttributes('body', 'paddingRight'); - } - - _resetElementAttributes(selector, styleProp) { - SelectorEngine.find(selector).forEach(element => { - const value = Manipulator.getDataAttribute(element, styleProp); - - if (typeof value === 'undefined' && element === document.body) { - element.style[styleProp] = ''; - } else { - Manipulator.removeDataAttribute(element, styleProp); - element.style[styleProp] = value; - } - }); - } - - _getScrollbarWidth() { - // thx d.walsh - const scrollDiv = document.createElement('div'); - scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER; - document.body.appendChild(scrollDiv); - const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; - document.body.removeChild(scrollDiv); - return scrollbarWidth; } // Static static jQueryInterface(config, relatedTarget) { return this.each(function () { - let data = Data.get(this, DATA_KEY$6); - const _config = { ...Default$5, - ...Manipulator.getDataAttributes(this), - ...(typeof config === 'object' && config ? config : {}) - }; + const data = Modal.getInstance(this) || new Modal(this, typeof config === 'object' ? config : {}); - if (!data) { - data = new Modal(this, _config); + if (typeof config !== 'string') { + return; } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - - data[config](relatedTarget); + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); } + + data[config](relatedTarget); }); } @@ -2929,7 +3040,7 @@ class Modal extends BaseComponent { EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) { const target = getElementFromSelector(this); - if (this.tagName === 'A' || this.tagName === 'AREA') { + if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault(); } @@ -2945,15 +3056,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, functi } }); }); - let data = Data.get(target, DATA_KEY$6); - - if (!data) { - const config = { ...Manipulator.getDataAttributes(target), - ...Manipulator.getDataAttributes(this) - }; - data = new Modal(target, config); - } - + const data = Modal.getInstance(target) || new Modal(target); data.toggle(this); }); /** @@ -2967,69 +3070,7 @@ defineJQueryPlugin(NAME$6, Modal); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): 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_STICKY_CONTENT = '.sticky-top'; - -const getWidth = () => { - // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes - const documentWidth = document.documentElement.clientWidth; - return Math.abs(window.innerWidth - documentWidth); -}; - -const hide = (width = getWidth()) => { - document.body.style.overflow = 'hidden'; - - _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width); - - _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width); - - _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width); -}; - -const _setElementAttributes = (selector, styleProp, callback) => { - const scrollbarWidth = getWidth(); - SelectorEngine.find(selector).forEach(element => { - if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) { - return; - } - - const actualValue = element.style[styleProp]; - const calculatedValue = window.getComputedStyle(element)[styleProp]; - Manipulator.setDataAttribute(element, styleProp, actualValue); - element.style[styleProp] = callback(Number.parseFloat(calculatedValue)) + 'px'; - }); -}; - -const reset = () => { - document.body.style.overflow = 'auto'; - - _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight'); - - _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight'); - - _resetElementAttributes('body', 'paddingRight'); -}; - -const _resetElementAttributes = (selector, styleProp) => { - SelectorEngine.find(selector).forEach(element => { - const value = Manipulator.getDataAttribute(element, styleProp); - - if (typeof value === 'undefined' && element === document.body) { - element.style.removeProperty(styleProp); - } else { - Manipulator.removeDataAttribute(element, styleProp); - element.style[styleProp] = value; - } - }); -}; - -/** - * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): offcanvas.js + * Bootstrap (v5.0.0): offcanvas.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -3055,11 +3096,8 @@ const DefaultType$4 = { keyboard: 'boolean', scroll: 'boolean' }; -const CLASS_NAME_BACKDROP_BODY = 'offcanvas-backdrop'; const CLASS_NAME_SHOW$4 = 'show'; -const CLASS_NAME_TOGGLING = 'offcanvas-toggling'; const OPEN_SELECTOR = '.offcanvas.show'; -const ACTIVE_SELECTOR = `${OPEN_SELECTOR}, .${CLASS_NAME_TOGGLING}`; const EVENT_SHOW$2 = `show${EVENT_KEY$5}`; const EVENT_SHOWN$2 = `shown${EVENT_KEY$5}`; const EVENT_HIDE$2 = `hide${EVENT_KEY$5}`; @@ -3067,6 +3105,7 @@ const EVENT_HIDDEN$2 = `hidden${EVENT_KEY$5}`; const EVENT_FOCUSIN = `focusin${EVENT_KEY$5}`; const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$5}${DATA_API_KEY$2}`; const EVENT_CLICK_DISMISS$1 = `click.dismiss${EVENT_KEY$5}`; +const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$5}`; const SELECTOR_DATA_DISMISS$1 = '[data-bs-dismiss="offcanvas"]'; const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle="offcanvas"]'; /** @@ -3080,6 +3119,7 @@ class Offcanvas extends BaseComponent { super(element); this._config = this._getConfig(config); this._isShown = false; + this._backdrop = this._initializeBackDrop(); this._addEventListeners(); } // Getters @@ -3114,15 +3154,13 @@ 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) { hide(); - } - this._element.classList.add(CLASS_NAME_TOGGLING); + this._enforceFocusOnElement(this._element); + } this._element.removeAttribute('aria-hidden'); @@ -3133,16 +3171,14 @@ class Offcanvas extends BaseComponent { this._element.classList.add(CLASS_NAME_SHOW$4); const completeCallBack = () => { - this._element.classList.remove(CLASS_NAME_TOGGLING); - EventHandler.trigger(this._element, EVENT_SHOWN$2, { 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() { @@ -3156,8 +3192,6 @@ class Offcanvas extends BaseComponent { return; } - this._element.classList.add(CLASS_NAME_TOGGLING); - EventHandler.off(document, EVENT_FOCUSIN); this._element.blur(); @@ -3166,6 +3200,8 @@ class Offcanvas extends BaseComponent { this._element.classList.remove(CLASS_NAME_SHOW$4); + this._backdrop.hide(); + const completeCallback = () => { this._element.setAttribute('aria-hidden', true); @@ -3175,20 +3211,25 @@ class Offcanvas extends BaseComponent { this._element.style.visibility = 'hidden'; - if (this._config.backdrop) { - document.body.classList.remove(CLASS_NAME_BACKDROP_BODY); - } - if (!this._config.scroll) { reset(); } EventHandler.trigger(this._element, EVENT_HIDDEN$2); - - 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 @@ -3201,6 +3242,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 @@ -3214,18 +3264,11 @@ class Offcanvas extends BaseComponent { _addEventListeners() { EventHandler.on(this._element, EVENT_CLICK_DISMISS$1, SELECTOR_DATA_DISMISS$1, () => 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$1, event => { - const target = SelectorEngine.findOne(getSelectorFromElement(event.target)); - - if (!this._element.contains(event.target) && target !== this._element) { - this.hide(); - } - }); } // Static @@ -3271,10 +3314,10 @@ EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, functi } }); // 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$5) || new Offcanvas(target); @@ -3293,7 +3336,7 @@ defineJQueryPlugin(NAME$5, Offcanvas); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): util/sanitizer.js + * Bootstrap (v5.0.0): util/sanitizer.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -3406,7 +3449,7 @@ function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) { /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): tooltip.js + * Bootstrap (v5.0.0): tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -3581,7 +3624,6 @@ class Tooltip extends BaseComponent { dispose() { clearTimeout(this._timeout); - EventHandler.off(this._element, this.constructor.EVENT_KEY); EventHandler.off(this._element.closest(`.${CLASS_NAME_MODAL}`), 'hide.bs.modal', this._hideModalHandler); if (this.tip && this.tip.parentNode) { @@ -3666,7 +3708,7 @@ class Tooltip extends BaseComponent { if ('ontouchstart' in document.documentElement) { [].concat(...document.body.children).forEach(element => { - EventHandler.on(element, 'mouseover', noop()); + EventHandler.on(element, 'mouseover', noop); }); } @@ -3865,7 +3907,6 @@ class Tooltip extends BaseComponent { modifiers: [{ name: 'flip', options: { - altBoundary: true, fallbackPlacements: this.config.fallbackPlacements } }, { @@ -4148,7 +4189,7 @@ defineJQueryPlugin(NAME$4, Tooltip); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): popover.js + * Bootstrap (v5.0.0): popover.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -4298,7 +4339,7 @@ defineJQueryPlugin(NAME$3, Popover); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): scrollspy.js + * Bootstrap (v5.0.0): scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -4411,6 +4452,7 @@ class ScrollSpy extends BaseComponent { _getConfig(config) { config = { ...Default$1, + ...Manipulator.getDataAttributes(this._element), ...(typeof config === 'object' && config ? config : {}) }; @@ -4519,21 +4561,17 @@ class ScrollSpy extends BaseComponent { static jQueryInterface(config) { return this.each(function () { - let data = Data.get(this, DATA_KEY$2); - - const _config = typeof config === 'object' && config; + const data = ScrollSpy.getInstance(this) || new ScrollSpy(this, typeof config === 'object' ? config : {}); - if (!data) { - data = new ScrollSpy(this, _config); + if (typeof config !== 'string') { + return; } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`); - } - - data[config](); + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); } + + data[config](); }); } @@ -4546,7 +4584,7 @@ class ScrollSpy extends BaseComponent { EventHandler.on(window, EVENT_LOAD_DATA_API, () => { - SelectorEngine.find(SELECTOR_DATA_SPY).forEach(spy => new ScrollSpy(spy, Manipulator.getDataAttributes(spy))); + SelectorEngine.find(SELECTOR_DATA_SPY).forEach(spy => new ScrollSpy(spy)); }); /** * ------------------------------------------------------------------------ @@ -4559,7 +4597,7 @@ defineJQueryPlugin(NAME$2, ScrollSpy); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): tab.js + * Bootstrap (v5.0.0): tab.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -4603,7 +4641,7 @@ class Tab extends BaseComponent { show() { - if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && this._element.classList.contains(CLASS_NAME_ACTIVE) || isDisabled(this._element)) { + if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && this._element.classList.contains(CLASS_NAME_ACTIVE)) { return; } @@ -4691,11 +4729,17 @@ class Tab extends BaseComponent { element.classList.add(CLASS_NAME_SHOW$1); } - if (element.parentNode && element.parentNode.classList.contains(CLASS_NAME_DROPDOWN_MENU)) { + let parent = element.parentNode; + + if (parent && parent.nodeName === 'LI') { + parent = parent.parentNode; + } + + if (parent && parent.classList.contains(CLASS_NAME_DROPDOWN_MENU)) { const dropdownElement = element.closest(SELECTOR_DROPDOWN); if (dropdownElement) { - SelectorEngine.find(SELECTOR_DROPDOWN_TOGGLE).forEach(dropdown => dropdown.classList.add(CLASS_NAME_ACTIVE)); + SelectorEngine.find(SELECTOR_DROPDOWN_TOGGLE, dropdownElement).forEach(dropdown => dropdown.classList.add(CLASS_NAME_ACTIVE)); } element.setAttribute('aria-expanded', true); @@ -4730,7 +4774,14 @@ class Tab extends BaseComponent { EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - event.preventDefault(); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + + if (isDisabled(this)) { + return; + } + const data = Data.get(this, DATA_KEY$1) || new Tab(this); data.show(); }); @@ -4745,7 +4796,7 @@ defineJQueryPlugin(NAME$1, Tab); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): toast.js + * Bootstrap (v5.0.0): toast.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ @@ -4884,7 +4935,6 @@ class Toast extends BaseComponent { this._element.classList.remove(CLASS_NAME_SHOW); } - EventHandler.off(this._element, EVENT_CLICK_DISMISS); super.dispose(); this._config = null; } // Private |