assets/javascripts/bootstrap.js in bootstrap-5.0.0.beta3 vs assets/javascripts/bootstrap.js in bootstrap-5.0.0

- old
+ new

@@ -1,7 +1,7 @@ /*! - * 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) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core')) : @@ -31,11 +31,11 @@ var Popper__namespace = /*#__PURE__*/_interopNamespace(Popper); /** * -------------------------------------------------------------------------- - * 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 MAX_UID = 1000000; const MILLISECONDS_MULTIPLIER = 1000; @@ -76,11 +76,11 @@ return null; } // Just in case some CMS puts out a full URL with the anchor appended if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) { - hrefAttr = '#' + hrefAttr.split('#')[1]; + hrefAttr = `#${hrefAttr.split('#')[1]}`; } selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null; } @@ -154,11 +154,11 @@ const expectedTypes = configTypes[property]; const value = config[property]; 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}".`); } }); }; const isVisible = element => { @@ -212,11 +212,11 @@ } return findShadowRoot(element.parentNode); }; - const noop = () => function () {}; + const noop = () => {}; const reflow = element => element.offsetHeight; const getjQuery = () => { const { @@ -256,13 +256,19 @@ }; } }); }; + 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) * -------------------------------------------------------------------------- */ /** @@ -312,11 +318,11 @@ }; /** * -------------------------------------------------------------------------- - * 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -332,10 +338,11 @@ let uidEvent = 1; 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']); /** * ------------------------------------------------------------------------ * Private methods * ------------------------------------------------------------------------ @@ -375,11 +382,11 @@ if (domElements[i] === target) { event.delegateTarget = target; 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]); } } @@ -404,19 +411,12 @@ return 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) { typeEvent = originalTypeEvent; } @@ -430,10 +430,28 @@ } 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); const events = getEvent(element); const handlers = events[typeEvent] || (events[typeEvent] = {}); @@ -473,10 +491,16 @@ removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector); } }); } + 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); }, @@ -525,11 +549,11 @@ if (typeof event !== 'string' || !element) { return null; } const $ = getjQuery(); - const typeEvent = event.replace(stripNameRegex, ''); + const typeEvent = getTypeEvent(event); const inNamespace = event !== typeEvent; const isNative = nativeEvents.has(typeEvent); let jQueryEvent; let bubbles = true; let nativeDispatch = true; @@ -583,21 +607,21 @@ }; /** * -------------------------------------------------------------------------- - * 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ - const VERSION = '5.0.0-beta3'; + const VERSION = '5.0.0'; class BaseComponent { constructor(element) { element = typeof element === 'string' ? document.querySelector(element) : element; @@ -609,10 +633,11 @@ Data.set(this._element, this.constructor.DATA_KEY, this); } dispose() { Data.remove(this._element, this.constructor.DATA_KEY); + EventHandler.off(this._element, `.${this.constructor.DATA_KEY}`); this._element = null; } /** Static */ @@ -626,31 +651,31 @@ } /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): alert.js + * Bootstrap (v5.0.0): alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ - 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'; const SELECTOR_DISMISS = '[data-bs-dismiss="alert"]'; 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 * ------------------------------------------------------------------------ */ @@ -682,13 +707,13 @@ _triggerCloseEvent(element) { return EventHandler.trigger(element, EVENT_CLOSE); } _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; } @@ -744,25 +769,25 @@ * jQuery * ------------------------------------------------------------------------ * 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ - 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'; const CLASS_NAME_ACTIVE$3 = 'active'; const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; @@ -824,15 +849,15 @@ * jQuery * ------------------------------------------------------------------------ * 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) * -------------------------------------------------------------------------- */ function normalizeData(val) { if (val === 'true') { @@ -902,11 +927,11 @@ }; /** * -------------------------------------------------------------------------- - * 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) * -------------------------------------------------------------------------- */ /** @@ -973,38 +998,38 @@ }; /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): carousel.js + * Bootstrap (v5.0.0): carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ - 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'; const ARROW_LEFT_KEY = 'ArrowLeft'; 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, pause: 'hover', wrap: true, touch: true }; - const DefaultType$8 = { + const DefaultType$9 = { interval: '(number|boolean)', keyboard: 'boolean', slide: '(boolean|string)', pause: '(string|boolean)', wrap: 'boolean', @@ -1071,11 +1096,11 @@ this._addEventListeners(); } // Getters static get Default() { - return Default$8; + return Default$9; } static get DATA_KEY() { return DATA_KEY$9; } // Public @@ -1156,11 +1181,10 @@ this._slide(order, this._items[index]); } dispose() { - EventHandler.off(this._element, EVENT_KEY$9); this._items = null; this._config = null; this._interval = null; this._isPaused = null; this._isSliding = null; @@ -1169,14 +1193,14 @@ super.dispose(); } // Private _getConfig(config) { - config = { ...Default$8, + config = { ...Default$9, ...config }; - typeCheckConfig(NAME$9, config, DefaultType$8); + typeCheckConfig(NAME$a, config, DefaultType$9); return config; } _handleSwipe() { const absDeltax = Math.abs(this.touchDeltaX); @@ -1271,15 +1295,15 @@ } 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); } } _getItemIndex(element) { this._items = element && element.parentNode ? SelectorEngine.find(SELECTOR_ITEM, element.parentNode) : []; @@ -1437,32 +1461,32 @@ if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) { return direction; } 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) { if (![ORDER_NEXT, ORDER_PREV].includes(order)) { return order; } 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) }; if (typeof config === 'object') { _config = { ..._config, @@ -1542,42 +1566,42 @@ * jQuery * ------------------------------------------------------------------------ * 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ - 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)' }; const EVENT_SHOW$5 = `show${EVENT_KEY$8}`; 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'; const WIDTH = 'width'; const HEIGHT = 'height'; @@ -1620,28 +1644,28 @@ } } // Getters static get Default() { - return Default$7; + return Default$8; } static get DATA_KEY() { return DATA_KEY$8; } // Public toggle() { - if (this._element.classList.contains(CLASS_NAME_SHOW$7)) { + if (this._element.classList.contains(CLASS_NAME_SHOW$8)) { this.hide(); } else { this.show(); } } show() { - if (this._isTransitioning || this._element.classList.contains(CLASS_NAME_SHOW$7)) { + if (this._isTransitioning || this._element.classList.contains(CLASS_NAME_SHOW$8)) { return; } let actives; let activesData; @@ -1707,11 +1731,11 @@ this.setTransitioning(true); 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); EventHandler.trigger(this._element, EVENT_SHOWN$5); }; @@ -1723,11 +1747,11 @@ emulateTransitionEnd(this._element, transitionDuration); this._element.style[dimension] = `${this._element[scrollSize]}px`; } hide() { - if (this._isTransitioning || !this._element.classList.contains(CLASS_NAME_SHOW$7)) { + if (this._isTransitioning || !this._element.classList.contains(CLASS_NAME_SHOW$8)) { return; } const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$5); @@ -1740,20 +1764,20 @@ this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; reflow(this._element); 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; if (triggerArrayLength > 0) { for (let i = 0; i < triggerArrayLength; i++) { 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); } } } @@ -1788,16 +1812,16 @@ this._isTransitioning = null; } // Private _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; } _getDimension() { return this._element.classList.contains(WIDTH) ? WIDTH : HEIGHT; @@ -1829,11 +1853,11 @@ _addAriaAndCollapsedClass(element, triggerArray) { if (!element || !triggerArray.length) { 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); } else { elem.classList.add(CLASS_NAME_COLLAPSED); @@ -1844,11 +1868,11 @@ } // Static 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 : {}) }; if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { @@ -1915,25 +1939,25 @@ * jQuery * ------------------------------------------------------------------------ * 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ - 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'; const ESCAPE_KEY$2 = 'Escape'; const SPACE_KEY = 'Space'; @@ -1949,12 +1973,11 @@ const EVENT_SHOWN$4 = `shown${EVENT_KEY$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'; const CLASS_NAME_NAVBAR = 'navbar'; const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]'; @@ -1965,23 +1988,25 @@ const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; 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)' }; /** * ------------------------------------------------------------------------ * Class Definition * ------------------------------------------------------------------------ @@ -1998,40 +2023,39 @@ this._addEventListeners(); } // Getters static get Default() { - return Default$6; + return Default$7; } static get DefaultType() { - return DefaultType$6; + return DefaultType$7; } static get DATA_KEY() { return DATA_KEY$7; } // Public 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); + const isActive = this._element.classList.contains(CLASS_NAME_SHOW$7); - Dropdown.clearMenus(); - if (isActive) { + this.hide(); return; } this.show(); } 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; } const parent = Dropdown.getParentFromElement(this._element); const relatedTarget = { @@ -2078,52 +2102,37 @@ // only needed because of broken event delegation on iOS // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html 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) { this._popper.destroy(); @@ -2147,20 +2156,47 @@ event.preventDefault(); this.toggle(); }); } + _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; } @@ -2233,10 +2269,33 @@ } 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 static dropdownInterface(element, config) { let data = Data.get(element, DATA_KEY$7); @@ -2266,71 +2325,51 @@ if (event) { if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY) { return; } - if (/input|select|textarea|form/i.test(event.target.tagName)) { + if (/input|select|option|textarea|form/i.test(event.target.tagName)) { return; } } const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE$3); 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())); + if (event.type === 'click') { + relatedTarget.clickEvent = event; + } } - toggles[i].setAttribute('aria-expanded', 'false'); - - if (context._popper) { - context._popper.destroy(); - } - - 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); } } static getParentFromElement(element) { return getElementFromSelector(element) || element.parentNode; @@ -2346,58 +2385,42 @@ // - If trigger inside the menu => not a dropdown command if (/input|textarea/i.test(event.target.tagName) ? event.key === SPACE_KEY || event.key !== ESCAPE_KEY$2 && (event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY || event.target.closest(SELECTOR_MENU)) : !REGEXP_KEYDOWN.test(event.key)) { 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; } if (!isActive || event.key === SPACE_KEY) { Dropdown.clearMenus(); 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); } } /** * ------------------------------------------------------------------------ @@ -2419,19 +2442,221 @@ * jQuery * ------------------------------------------------------------------------ * 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) + * -------------------------------------------------------------------------- + */ + /** * ------------------------------------------------------------------------ * Constants * ------------------------------------------------------------------------ */ @@ -2456,26 +2681,22 @@ const EVENT_SHOW$3 = `show${EVENT_KEY$6}`; 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'; const CLASS_NAME_STATIC = 'modal-static'; 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 * ------------------------------------------------------------------------ */ @@ -2483,16 +2704,14 @@ class Modal extends BaseComponent { constructor(element, config) { 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 static get Default() { return Default$5; @@ -2523,15 +2742,13 @@ if (this._isShown || showEvent.defaultPrevented) { return; } this._isShown = true; + hide(); + document.body.classList.add(CLASS_NAME_OPEN); - this._checkScrollbar(); - - this._setScrollbar(); - this._adjustDialog(); this._setEscapeEvent(); this._setResizeEvent(); @@ -2590,36 +2807,46 @@ this._hideModal(); } } 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` * Do not move `document` in `htmlElements` array * It will remove `EVENT_CLICK_DATA_API` event that should remain */ 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() { this._adjustDialog(); } // 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); return config; } @@ -2688,20 +2915,20 @@ }); } _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(); } else if (!this._config.keyboard && event.key === ESCAPE_KEY$1) { this._triggerBackdropTransition(); } }); } else { - EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS); + EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS$1); } } _setResizeEvent() { if (this._isShown) { @@ -2720,89 +2947,39 @@ this._element.removeAttribute('role'); 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); + EventHandler.on(this._element, EVENT_CLICK_DISMISS$2, event => { + if (this._ignoreBackdropClick) { + this._ignoreBackdropClick = false; + return; } - 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); - } - - 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() { return this._element.classList.contains(CLASS_NAME_FADE$4); } @@ -2842,107 +3019,41 @@ // ---------------------------------------------------------------------- _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); }); } } /** @@ -2953,11 +3064,11 @@ 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(); } EventHandler.one(target, EVENT_SHOW$3, showEvent => { if (showEvent.defaultPrevented) { @@ -2969,19 +3080,11 @@ if (isVisible(this)) { this.focus(); } }); }); - 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); }); /** * ------------------------------------------------------------------------ * jQuery @@ -2991,73 +3094,11 @@ 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -3079,22 +3120,20 @@ const DefaultType$4 = { backdrop: 'boolean', 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}`; 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"]'; /** * ------------------------------------------------------------------------ * Class Definition @@ -3104,10 +3143,11 @@ class Offcanvas extends BaseComponent { constructor(element, config) { super(element); this._config = this._getConfig(config); this._isShown = false; + this._backdrop = this._initializeBackDrop(); this._addEventListeners(); } // Getters @@ -3138,39 +3178,35 @@ } 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._enforceFocusOnElement(this._element); } - 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$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() { if (!this._isShown) { return; @@ -3180,43 +3216,48 @@ if (hideEvent.defaultPrevented) { 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$4); + this._backdrop.hide(); + const completeCallback = () => { this._element.setAttribute('aria-hidden', true); this._element.removeAttribute('aria-modal'); 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) { 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 _getConfig(config) { config = { ...Default$4, @@ -3225,10 +3266,19 @@ }; typeCheckConfig(NAME$5, config, DefaultType$4); 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 => { if (document !== event.target && element !== event.target && !element.contains(event.target)) { @@ -3238,22 +3288,15 @@ element.focus(); } _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 static jQueryInterface(config) { return this.each(function () { @@ -3295,14 +3338,14 @@ if (isVisible(this)) { this.focus(); } }); // 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); data.toggle(this); }); @@ -3317,11 +3360,11 @@ 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) * -------------------------------------------------------------------------- */ const uriAttrs = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']); const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; @@ -3430,11 +3473,11 @@ return createdDocument.body.innerHTML; } /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): tooltip.js + * Bootstrap (v5.0.0): tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -3605,11 +3648,10 @@ } } 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) { this.tip.parentNode.removeChild(this.tip); } @@ -3690,11 +3732,11 @@ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html if ('ontouchstart' in document.documentElement) { [].concat(...document.body.children).forEach(element => { - EventHandler.on(element, 'mouseover', noop()); + EventHandler.on(element, 'mouseover', noop); }); } const complete = () => { const prevHoverState = this._hoverState; @@ -3889,11 +3931,10 @@ const defaultBsPopperConfig = { placement: attachment, modifiers: [{ name: 'flip', options: { - altBoundary: true, fallbackPlacements: this.config.fallbackPlacements } }, { name: 'offset', options: { @@ -4172,11 +4213,11 @@ 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -4322,11 +4363,11 @@ 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -4435,10 +4476,11 @@ } // Private _getConfig(config) { config = { ...Default$1, + ...Manipulator.getDataAttributes(this._element), ...(typeof config === 'object' && config ? config : {}) }; if (typeof config.target !== 'string' && isElement(config.target)) { let { @@ -4543,25 +4585,21 @@ } // Static static jQueryInterface(config) { return this.each(function () { - let data = Data.get(this, DATA_KEY$2); + const data = ScrollSpy.getInstance(this) || new ScrollSpy(this, typeof config === 'object' ? config : {}); - const _config = 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](); }); } } /** @@ -4570,11 +4608,11 @@ * ------------------------------------------------------------------------ */ 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)); }); /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ @@ -4583,11 +4621,11 @@ 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -4627,11 +4665,11 @@ return DATA_KEY$1; } // Public 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; } let previous; const target = getElementFromSelector(this._element); @@ -4715,15 +4753,21 @@ if (element.classList.contains(CLASS_NAME_FADE$1)) { 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); } @@ -4754,11 +4798,18 @@ * ------------------------------------------------------------------------ */ 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(); }); /** * ------------------------------------------------------------------------ @@ -4769,11 +4820,11 @@ 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) * -------------------------------------------------------------------------- */ /** * ------------------------------------------------------------------------ @@ -4908,11 +4959,10 @@ if (this._element.classList.contains(CLASS_NAME_SHOW)) { this._element.classList.remove(CLASS_NAME_SHOW); } - EventHandler.off(this._element, EVENT_CLICK_DISMISS); super.dispose(); this._config = null; } // Private @@ -4966,10 +5016,10 @@ defineJQueryPlugin(NAME, Toast); /** * -------------------------------------------------------------------------- - * Bootstrap (v5.0.0-beta3): index.umd.js + * Bootstrap (v5.0.0): index.umd.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ var index_umd = { Alert,