From f3b4308cb59bf4b21ff186f8479c82239446d139 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 7 Jun 2021 12:56:27 +0200 Subject: upgrade to bootstrap 5.0.1 and first batch of fixes --- vendor/twbs/bootstrap/js/src/alert.js | 101 ++-- vendor/twbs/bootstrap/js/src/button.js | 158 +------ vendor/twbs/bootstrap/js/src/carousel.js | 424 +++++++++-------- vendor/twbs/bootstrap/js/src/collapse.js | 302 ++++++------ vendor/twbs/bootstrap/js/src/dropdown.js | 531 +++++++++++---------- vendor/twbs/bootstrap/js/src/modal.js | 505 +++++++------------- vendor/twbs/bootstrap/js/src/popover.js | 89 ++-- vendor/twbs/bootstrap/js/src/scrollspy.js | 204 ++++---- vendor/twbs/bootstrap/js/src/tab.js | 168 +++---- vendor/twbs/bootstrap/js/src/toast.js | 158 ++++--- vendor/twbs/bootstrap/js/src/tools/sanitizer.js | 127 ----- vendor/twbs/bootstrap/js/src/tooltip.js | 594 ++++++++++++------------ vendor/twbs/bootstrap/js/src/util.js | 198 -------- 13 files changed, 1403 insertions(+), 2156 deletions(-) delete mode 100644 vendor/twbs/bootstrap/js/src/tools/sanitizer.js delete mode 100644 vendor/twbs/bootstrap/js/src/util.js (limited to 'vendor/twbs/bootstrap/js/src') diff --git a/vendor/twbs/bootstrap/js/src/alert.js b/vendor/twbs/bootstrap/js/src/alert.js index afd7736c7..87cc7e731 100644 --- a/vendor/twbs/bootstrap/js/src/alert.js +++ b/vendor/twbs/bootstrap/js/src/alert.js @@ -1,12 +1,17 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): alert.js + * Bootstrap (v5.0.1): alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElementFromSelector +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,13 +20,11 @@ import Util from './util' */ const NAME = 'alert' -const VERSION = '4.6.0' const DATA_KEY = 'bs.alert' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const SELECTOR_DISMISS = '[data-dismiss="alert"]' +const SELECTOR_DISMISS = '[data-bs-dismiss="alert"]' const EVENT_CLOSE = `close${EVENT_KEY}` const EVENT_CLOSED = `closed${EVENT_KEY}` @@ -37,95 +40,59 @@ const CLASS_NAME_SHOW = 'show' * ------------------------------------------------------------------------ */ -class Alert { - constructor(element) { - this._element = element - } - +class Alert extends BaseComponent { // Getters - static get VERSION() { - return VERSION + static get NAME() { + return NAME } // Public close(element) { - let rootElement = this._element - if (element) { - rootElement = this._getRootElement(element) - } - + const rootElement = element ? this._getRootElement(element) : this._element const customEvent = this._triggerCloseEvent(rootElement) - if (customEvent.isDefaultPrevented()) { + if (customEvent === null || customEvent.defaultPrevented) { return } this._removeElement(rootElement) } - dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null - } - // Private _getRootElement(element) { - const selector = Util.getSelectorFromElement(element) - let parent = false - - if (selector) { - parent = document.querySelector(selector) - } - - if (!parent) { - parent = $(element).closest(`.${CLASS_NAME_ALERT}`)[0] - } - - return parent + return getElementFromSelector(element) || element.closest(`.${CLASS_NAME_ALERT}`) } _triggerCloseEvent(element) { - const closeEvent = $.Event(EVENT_CLOSE) - - $(element).trigger(closeEvent) - return closeEvent + return EventHandler.trigger(element, EVENT_CLOSE) } _removeElement(element) { - $(element).removeClass(CLASS_NAME_SHOW) - - if (!$(element).hasClass(CLASS_NAME_FADE)) { - this._destroyElement(element) - return - } + element.classList.remove(CLASS_NAME_SHOW) - const transitionDuration = Util.getTransitionDurationFromElement(element) - - $(element) - .one(Util.TRANSITION_END, event => this._destroyElement(element, event)) - .emulateTransitionEnd(transitionDuration) + const isAnimated = element.classList.contains(CLASS_NAME_FADE) + this._queueCallback(() => this._destroyElement(element), element, isAnimated) } _destroyElement(element) { - $(element) - .detach() - .trigger(EVENT_CLOSED) - .remove() + if (element.parentNode) { + element.parentNode.removeChild(element) + } + + EventHandler.trigger(element, EVENT_CLOSED) } // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) + let data = Data.get(this, DATA_KEY) if (!data) { data = new Alert(this) - $element.data(DATA_KEY, data) } if (config === 'close') { @@ -134,7 +101,7 @@ class Alert { }) } - static _handleDismiss(alertInstance) { + static handleDismiss(alertInstance) { return function (event) { if (event) { event.preventDefault() @@ -151,23 +118,15 @@ class Alert { * ------------------------------------------------------------------------ */ -$(document).on( - EVENT_CLICK_DATA_API, - SELECTOR_DISMISS, - Alert._handleDismiss(new Alert()) -) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert())) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Alert to jQuery only if jQuery is present */ -$.fn[NAME] = Alert._jQueryInterface -$.fn[NAME].Constructor = Alert -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Alert._jQueryInterface -} +defineJQueryPlugin(Alert) export default Alert diff --git a/vendor/twbs/bootstrap/js/src/button.js b/vendor/twbs/bootstrap/js/src/button.js index 316387e8e..6ef753136 100644 --- a/vendor/twbs/bootstrap/js/src/button.js +++ b/vendor/twbs/bootstrap/js/src/button.js @@ -1,11 +1,14 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): button.js + * Bootstrap (v5.0.1): button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import { defineJQueryPlugin } from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -14,28 +17,15 @@ import $ from 'jquery' */ const NAME = 'button' -const VERSION = '4.6.0' const DATA_KEY = 'bs.button' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const CLASS_NAME_ACTIVE = 'active' -const CLASS_NAME_BUTTON = 'btn' -const CLASS_NAME_FOCUS = 'focus' -const SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]' -const SELECTOR_DATA_TOGGLES = '[data-toggle="buttons"]' -const SELECTOR_DATA_TOGGLE = '[data-toggle="button"]' -const SELECTOR_DATA_TOGGLES_BUTTONS = '[data-toggle="buttons"] .btn' -const SELECTOR_INPUT = 'input:not([type="hidden"])' -const SELECTOR_ACTIVE = '.active' -const SELECTOR_BUTTON = '.btn' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]' const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` -const EVENT_FOCUS_BLUR_DATA_API = `focus${EVENT_KEY}${DATA_API_KEY} ` + - `blur${EVENT_KEY}${DATA_API_KEY}` -const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` /** * ------------------------------------------------------------------------ @@ -43,87 +33,30 @@ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` * ------------------------------------------------------------------------ */ -class Button { - constructor(element) { - this._element = element - this.shouldAvoidTriggerChange = false - } - +class Button extends BaseComponent { // Getters - static get VERSION() { - return VERSION + static get NAME() { + return NAME } // Public toggle() { - let triggerChangeEvent = true - let addAriaPressed = true - const rootElement = $(this._element).closest(SELECTOR_DATA_TOGGLES)[0] - - if (rootElement) { - const input = this._element.querySelector(SELECTOR_INPUT) - - if (input) { - if (input.type === 'radio') { - if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) { - triggerChangeEvent = false - } else { - const activeElement = rootElement.querySelector(SELECTOR_ACTIVE) - - if (activeElement) { - $(activeElement).removeClass(CLASS_NAME_ACTIVE) - } - } - } - - if (triggerChangeEvent) { - // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input - if (input.type === 'checkbox' || input.type === 'radio') { - input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE) - } - - if (!this.shouldAvoidTriggerChange) { - $(input).trigger('change') - } - } - - input.focus() - addAriaPressed = false - } - } - - if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) { - if (addAriaPressed) { - this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE)) - } - - if (triggerChangeEvent) { - $(this._element).toggleClass(CLASS_NAME_ACTIVE) - } - } - } - - dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null + // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method + this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE)) } // Static - static _jQueryInterface(config, avoidTriggerChange) { + static jQueryInterface(config) { return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) + let data = Data.get(this, DATA_KEY) if (!data) { data = new Button(this) - $element.data(DATA_KEY, data) } - data.shouldAvoidTriggerChange = avoidTriggerChange - if (config === 'toggle') { data[config]() } @@ -137,73 +70,26 @@ class Button { * ------------------------------------------------------------------------ */ -$(document) - .on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, event => { - let button = event.target - const initialButton = button - - if (!$(button).hasClass(CLASS_NAME_BUTTON)) { - button = $(button).closest(SELECTOR_BUTTON)[0] - } - - if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) { - event.preventDefault() // work around Firefox bug #1540995 - } else { - const inputBtn = button.querySelector(SELECTOR_INPUT) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { + event.preventDefault() - if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) { - event.preventDefault() // work around Firefox bug #1540995 - return - } + const button = event.target.closest(SELECTOR_DATA_TOGGLE) - if (initialButton.tagName === 'INPUT' || button.tagName !== 'LABEL') { - Button._jQueryInterface.call($(button), 'toggle', initialButton.tagName === 'INPUT') - } - } - }) - .on(EVENT_FOCUS_BLUR_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, event => { - const button = $(event.target).closest(SELECTOR_BUTTON)[0] - $(button).toggleClass(CLASS_NAME_FOCUS, /^focus(in)?$/.test(event.type)) - }) - -$(window).on(EVENT_LOAD_DATA_API, () => { - // ensure correct active class is set to match the controls' actual values/states - - // find all checkboxes/readio buttons inside data-toggle groups - let buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLES_BUTTONS)) - for (let i = 0, len = buttons.length; i < len; i++) { - const button = buttons[i] - const input = button.querySelector(SELECTOR_INPUT) - if (input.checked || input.hasAttribute('checked')) { - button.classList.add(CLASS_NAME_ACTIVE) - } else { - button.classList.remove(CLASS_NAME_ACTIVE) - } + let data = Data.get(button, DATA_KEY) + if (!data) { + data = new Button(button) } - // find all button toggles - buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)) - for (let i = 0, len = buttons.length; i < len; i++) { - const button = buttons[i] - if (button.getAttribute('aria-pressed') === 'true') { - button.classList.add(CLASS_NAME_ACTIVE) - } else { - button.classList.remove(CLASS_NAME_ACTIVE) - } - } + data.toggle() }) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Button to jQuery only if jQuery is present */ -$.fn[NAME] = Button._jQueryInterface -$.fn[NAME].Constructor = Button -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Button._jQueryInterface -} +defineJQueryPlugin(Button) export default Button diff --git a/vendor/twbs/bootstrap/js/src/carousel.js b/vendor/twbs/bootstrap/js/src/carousel.js index b63d406bd..bb894e9c3 100644 --- a/vendor/twbs/bootstrap/js/src/carousel.js +++ b/vendor/twbs/bootstrap/js/src/carousel.js @@ -1,12 +1,24 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): carousel.js + * Bootstrap (v5.0.1): carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElementFromSelector, + isRTL, + isVisible, + reflow, + triggerTransitionEnd, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,13 +27,12 @@ import Util from './util' */ const NAME = 'carousel' -const VERSION = '4.6.0' const DATA_KEY = 'bs.carousel' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key -const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key + +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 @@ -43,8 +54,8 @@ const DefaultType = { touch: 'boolean' } -const DIRECTION_NEXT = 'next' -const DIRECTION_PREV = 'prev' +const ORDER_NEXT = 'next' +const ORDER_PREV = 'prev' const DIRECTION_LEFT = 'left' const DIRECTION_RIGHT = 'right' @@ -65,8 +76,8 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_CAROUSEL = 'carousel' const CLASS_NAME_ACTIVE = 'active' const CLASS_NAME_SLIDE = 'slide' -const CLASS_NAME_RIGHT = 'carousel-item-right' -const CLASS_NAME_LEFT = 'carousel-item-left' +const CLASS_NAME_END = 'carousel-item-end' +const CLASS_NAME_START = 'carousel-item-start' const CLASS_NAME_NEXT = 'carousel-item-next' const CLASS_NAME_PREV = 'carousel-item-prev' const CLASS_NAME_POINTER_EVENT = 'pointer-event' @@ -77,21 +88,22 @@ const SELECTOR_ITEM = '.carousel-item' const SELECTOR_ITEM_IMG = '.carousel-item img' const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev' const SELECTOR_INDICATORS = '.carousel-indicators' -const SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]' -const SELECTOR_DATA_RIDE = '[data-ride="carousel"]' +const SELECTOR_INDICATOR = '[data-bs-target]' +const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]' +const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]' -const PointerType = { - TOUCH: 'touch', - PEN: 'pen' -} +const POINTER_TYPE_TOUCH = 'touch' +const POINTER_TYPE_PEN = 'pen' /** * ------------------------------------------------------------------------ * Class Definition * ------------------------------------------------------------------------ */ -class Carousel { +class Carousel extends BaseComponent { constructor(element, config) { + super(element) + this._items = null this._interval = null this._activeElement = null @@ -102,45 +114,42 @@ class Carousel { this.touchDeltaX = 0 this._config = this._getConfig(config) - this._element = element - this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS) + this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element) this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 - this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent) + this._pointerEvent = Boolean(window.PointerEvent) this._addEventListeners() } // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public next() { if (!this._isSliding) { - this._slide(DIRECTION_NEXT) + this._slide(ORDER_NEXT) } } nextWhenVisible() { - const $element = $(this._element) // Don't call next when the page isn't visible // or the carousel or its parent isn't visible - if (!document.hidden && - ($element.is(':visible') && $element.css('visibility') !== 'hidden')) { + if (!document.hidden && isVisible(this._element)) { this.next() } } prev() { if (!this._isSliding) { - this._slide(DIRECTION_PREV) + this._slide(ORDER_PREV) } } @@ -149,8 +158,8 @@ class Carousel { this._isPaused = true } - if (this._element.querySelector(SELECTOR_NEXT_PREV)) { - Util.triggerTransitionEnd(this._element) + if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) { + triggerTransitionEnd(this._element) this.cycle(true) } @@ -168,7 +177,7 @@ class Carousel { this._interval = null } - if (this._config.interval && !this._isPaused) { + if (this._config && this._config.interval && !this._isPaused) { this._updateInterval() this._interval = setInterval( @@ -179,8 +188,7 @@ class Carousel { } to(index) { - this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM) - + this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element) const activeIndex = this._getItemIndex(this._activeElement) if (index > this._items.length - 1 || index < 0) { @@ -188,7 +196,7 @@ class Carousel { } if (this._isSliding) { - $(this._element).one(EVENT_SLID, () => this.to(index)) + EventHandler.one(this._element, EVENT_SLID, () => this.to(index)) return } @@ -198,25 +206,11 @@ class Carousel { return } - const direction = index > activeIndex ? - DIRECTION_NEXT : - DIRECTION_PREV + const order = index > activeIndex ? + ORDER_NEXT : + ORDER_PREV - this._slide(direction, this._items[index]) - } - - dispose() { - $(this._element).off(EVENT_KEY) - $.removeData(this._element, DATA_KEY) - - this._items = null - this._config = null - this._element = null - this._interval = null - this._isPaused = null - this._isSliding = null - this._activeElement = null - this._indicatorsElement = null + this._slide(order, this._items[index]) } // Private @@ -226,7 +220,7 @@ class Carousel { ...Default, ...config } - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } @@ -241,58 +235,47 @@ class Carousel { this.touchDeltaX = 0 - // swipe left - if (direction > 0) { - this.prev() + if (!direction) { + return } - // swipe right - if (direction < 0) { - this.next() - } + this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT) } _addEventListeners() { if (this._config.keyboard) { - $(this._element).on(EVENT_KEYDOWN, event => this._keydown(event)) + EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)) } if (this._config.pause === 'hover') { - $(this._element) - .on(EVENT_MOUSEENTER, event => this.pause(event)) - .on(EVENT_MOUSELEAVE, event => this.cycle(event)) + EventHandler.on(this._element, EVENT_MOUSEENTER, event => this.pause(event)) + EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event)) } - if (this._config.touch) { + if (this._config.touch && this._touchSupported) { this._addTouchEventListeners() } } _addTouchEventListeners() { - if (!this._touchSupported) { - return - } - const start = event => { - if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { - this.touchStartX = event.originalEvent.clientX + if (this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)) { + this.touchStartX = event.clientX } else if (!this._pointerEvent) { - this.touchStartX = event.originalEvent.touches[0].clientX + this.touchStartX = event.touches[0].clientX } } const move = event => { // ensure swiping with one touch and not pinching - if (event.originalEvent.touches && event.originalEvent.touches.length > 1) { - this.touchDeltaX = 0 - } else { - this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX - } + this.touchDeltaX = event.touches && event.touches.length > 1 ? + 0 : + event.touches[0].clientX - this.touchStartX } const end = event => { - if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { - this.touchDeltaX = event.originalEvent.clientX - this.touchStartX + if (this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)) { + this.touchDeltaX = event.clientX - this.touchStartX } this._handleSwipe() @@ -314,18 +297,19 @@ class Carousel { } } - $(this._element.querySelectorAll(SELECTOR_ITEM_IMG)) - .on(EVENT_DRAG_START, e => e.preventDefault()) + SelectorEngine.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => { + EventHandler.on(itemImg, EVENT_DRAG_START, e => e.preventDefault()) + }) if (this._pointerEvent) { - $(this._element).on(EVENT_POINTERDOWN, event => start(event)) - $(this._element).on(EVENT_POINTERUP, event => end(event)) + EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event)) + EventHandler.on(this._element, EVENT_POINTERUP, event => end(event)) this._element.classList.add(CLASS_NAME_POINTER_EVENT) } else { - $(this._element).on(EVENT_TOUCHSTART, event => start(event)) - $(this._element).on(EVENT_TOUCHMOVE, event => move(event)) - $(this._element).on(EVENT_TOUCHEND, event => end(event)) + EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event)) + EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event)) + EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event)) } } @@ -334,83 +318,81 @@ class Carousel { return } - switch (event.which) { - case ARROW_LEFT_KEYCODE: - event.preventDefault() - this.prev() - break - case ARROW_RIGHT_KEYCODE: - event.preventDefault() - this.next() - break - default: + if (event.key === ARROW_LEFT_KEY) { + event.preventDefault() + this._slide(DIRECTION_RIGHT) + } else if (event.key === ARROW_RIGHT_KEY) { + event.preventDefault() + this._slide(DIRECTION_LEFT) } } _getItemIndex(element) { this._items = element && element.parentNode ? - [].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) : + SelectorEngine.find(SELECTOR_ITEM, element.parentNode) : [] + return this._items.indexOf(element) } - _getItemByDirection(direction, activeElement) { - const isNextDirection = direction === DIRECTION_NEXT - const isPrevDirection = direction === DIRECTION_PREV + _getItemByOrder(order, activeElement) { + const isNext = order === ORDER_NEXT + const isPrev = order === ORDER_PREV const activeIndex = this._getItemIndex(activeElement) const lastItemIndex = this._items.length - 1 - const isGoingToWrap = isPrevDirection && activeIndex === 0 || - isNextDirection && activeIndex === lastItemIndex + const isGoingToWrap = (isPrev && activeIndex === 0) || (isNext && activeIndex === lastItemIndex) if (isGoingToWrap && !this._config.wrap) { return activeElement } - const delta = direction === DIRECTION_PREV ? -1 : 1 + const delta = isPrev ? -1 : 1 const itemIndex = (activeIndex + delta) % this._items.length return itemIndex === -1 ? - this._items[this._items.length - 1] : this._items[itemIndex] + this._items[this._items.length - 1] : + this._items[itemIndex] } _triggerSlideEvent(relatedTarget, eventDirectionName) { const targetIndex = this._getItemIndex(relatedTarget) - const fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM)) - const slideEvent = $.Event(EVENT_SLIDE, { + const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)) + + return EventHandler.trigger(this._element, EVENT_SLIDE, { relatedTarget, direction: eventDirectionName, from: fromIndex, to: targetIndex }) - - $(this._element).trigger(slideEvent) - - return slideEvent } _setActiveIndicatorElement(element) { if (this._indicatorsElement) { - const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE)) - $(indicators).removeClass(CLASS_NAME_ACTIVE) + const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement) - const nextIndicator = this._indicatorsElement.children[ - this._getItemIndex(element) - ] + activeIndicator.classList.remove(CLASS_NAME_ACTIVE) + activeIndicator.removeAttribute('aria-current') - if (nextIndicator) { - $(nextIndicator).addClass(CLASS_NAME_ACTIVE) + const indicators = SelectorEngine.find(SELECTOR_INDICATOR, this._indicatorsElement) + + for (let i = 0; i < indicators.length; i++) { + if (Number.parseInt(indicators[i].getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) { + indicators[i].classList.add(CLASS_NAME_ACTIVE) + indicators[i].setAttribute('aria-current', 'true') + break + } } } } _updateInterval() { - const element = this._activeElement || this._element.querySelector(SELECTOR_ACTIVE_ITEM) + const element = this._activeElement || SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element) if (!element) { return } - const elementInterval = parseInt(element.getAttribute('data-interval'), 10) + const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10) if (elementInterval) { this._config.defaultInterval = this._config.defaultInterval || this._config.interval @@ -420,35 +402,27 @@ class Carousel { } } - _slide(direction, element) { - const activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM) + _slide(directionOrOrder, element) { + const order = this._directionToOrder(directionOrOrder) + const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element) const activeElementIndex = this._getItemIndex(activeElement) - const nextElement = element || activeElement && - this._getItemByDirection(direction, activeElement) + const nextElement = element || this._getItemByOrder(order, activeElement) + const nextElementIndex = this._getItemIndex(nextElement) const isCycling = Boolean(this._interval) - let directionalClassName - let orderClassName - let eventDirectionName - - if (direction === DIRECTION_NEXT) { - directionalClassName = CLASS_NAME_LEFT - orderClassName = CLASS_NAME_NEXT - eventDirectionName = DIRECTION_LEFT - } else { - directionalClassName = CLASS_NAME_RIGHT - orderClassName = CLASS_NAME_PREV - eventDirectionName = DIRECTION_RIGHT - } + const isNext = order === ORDER_NEXT + const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END + const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV + const eventDirectionName = this._orderToDirection(order) - if (nextElement && $(nextElement).hasClass(CLASS_NAME_ACTIVE)) { + if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) { this._isSliding = false return } const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName) - if (slideEvent.isDefaultPrevented()) { + if (slideEvent.defaultPrevented) { return } @@ -466,42 +440,41 @@ class Carousel { this._setActiveIndicatorElement(nextElement) this._activeElement = nextElement - const slidEvent = $.Event(EVENT_SLID, { - relatedTarget: nextElement, - direction: eventDirectionName, - from: activeElementIndex, - to: nextElementIndex - }) + const triggerSlidEvent = () => { + EventHandler.trigger(this._element, EVENT_SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }) + } - if ($(this._element).hasClass(CLASS_NAME_SLIDE)) { - $(nextElement).addClass(orderClassName) + if (this._element.classList.contains(CLASS_NAME_SLIDE)) { + nextElement.classList.add(orderClassName) - Util.reflow(nextElement) + reflow(nextElement) - $(activeElement).addClass(directionalClassName) - $(nextElement).addClass(directionalClassName) + activeElement.classList.add(directionalClassName) + nextElement.classList.add(directionalClassName) - const transitionDuration = Util.getTransitionDurationFromElement(activeElement) + const completeCallBack = () => { + nextElement.classList.remove(directionalClassName, orderClassName) + nextElement.classList.add(CLASS_NAME_ACTIVE) - $(activeElement) - .one(Util.TRANSITION_END, () => { - $(nextElement) - .removeClass(`${directionalClassName} ${orderClassName}`) - .addClass(CLASS_NAME_ACTIVE) + activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName) - $(activeElement).removeClass(`${CLASS_NAME_ACTIVE} ${orderClassName} ${directionalClassName}`) + this._isSliding = false - this._isSliding = false + setTimeout(triggerSlidEvent, 0) + } - setTimeout(() => $(this._element).trigger(slidEvent), 0) - }) - .emulateTransitionEnd(transitionDuration) + this._queueCallback(completeCallBack, activeElement, true) } else { - $(activeElement).removeClass(CLASS_NAME_ACTIVE) - $(nextElement).addClass(CLASS_NAME_ACTIVE) + activeElement.classList.remove(CLASS_NAME_ACTIVE) + nextElement.classList.add(CLASS_NAME_ACTIVE) this._isSliding = false - $(this._element).trigger(slidEvent) + triggerSlidEvent() } if (isCycling) { @@ -509,72 +482,93 @@ class Carousel { } } + _directionToOrder(direction) { + if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) { + return direction + } + + if (isRTL()) { + return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT + } + + return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV + } + + _orderToDirection(order) { + if (![ORDER_NEXT, ORDER_PREV].includes(order)) { + return order + } + + if (isRTL()) { + return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT + } + + return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT + } + // Static - static _jQueryInterface(config) { - return this.each(function () { - let data = $(this).data(DATA_KEY) - let _config = { - ...Default, - ...$(this).data() - } + static carouselInterface(element, config) { + let data = Data.get(element, DATA_KEY) + let _config = { + ...Default, + ...Manipulator.getDataAttributes(element) + } - if (typeof config === 'object') { - _config = { - ..._config, - ...config - } + if (typeof config === 'object') { + _config = { + ..._config, + ...config } + } + + const action = typeof config === 'string' ? config : _config.slide - const action = typeof config === 'string' ? config : _config.slide + if (!data) { + data = new Carousel(element, _config) + } - if (!data) { - data = new Carousel(this, _config) - $(this).data(DATA_KEY, data) + if (typeof config === 'number') { + data.to(config) + } else if (typeof action === 'string') { + if (typeof data[action] === 'undefined') { + throw new TypeError(`No method named "${action}"`) } - if (typeof config === 'number') { - data.to(config) - } else if (typeof action === 'string') { - if (typeof data[action] === 'undefined') { - throw new TypeError(`No method named "${action}"`) - } + data[action]() + } else if (_config.interval && _config.ride) { + data.pause() + data.cycle() + } + } - data[action]() - } else if (_config.interval && _config.ride) { - data.pause() - data.cycle() - } + static jQueryInterface(config) { + return this.each(function () { + Carousel.carouselInterface(this, config) }) } - static _dataApiClickHandler(event) { - const selector = Util.getSelectorFromElement(this) - - if (!selector) { - return - } - - const target = $(selector)[0] + static dataApiClickHandler(event) { + const target = getElementFromSelector(this) - if (!target || !$(target).hasClass(CLASS_NAME_CAROUSEL)) { + if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { return } const config = { - ...$(target).data(), - ...$(this).data() + ...Manipulator.getDataAttributes(target), + ...Manipulator.getDataAttributes(this) } - const slideIndex = this.getAttribute('data-slide-to') + const slideIndex = this.getAttribute('data-bs-slide-to') if (slideIndex) { config.interval = false } - Carousel._jQueryInterface.call($(target), config) + Carousel.carouselInterface(target, config) if (slideIndex) { - $(target).data(DATA_KEY).to(slideIndex) + Data.get(target, DATA_KEY).to(slideIndex) } event.preventDefault() @@ -587,13 +581,13 @@ class Carousel { * ------------------------------------------------------------------------ */ -$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler) + +EventHandler.on(window, EVENT_LOAD_DATA_API, () => { + const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE) -$(window).on(EVENT_LOAD_DATA_API, () => { - const carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE)) for (let i = 0, len = carousels.length; i < len; i++) { - const $carousel = $(carousels[i]) - Carousel._jQueryInterface.call($carousel, $carousel.data()) + Carousel.carouselInterface(carousels[i], Data.get(carousels[i], DATA_KEY)) } }) @@ -601,13 +595,9 @@ $(window).on(EVENT_LOAD_DATA_API, () => { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Carousel to jQuery only if jQuery is present */ -$.fn[NAME] = Carousel._jQueryInterface -$.fn[NAME].Constructor = Carousel -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Carousel._jQueryInterface -} +defineJQueryPlugin(Carousel) export default Carousel diff --git a/vendor/twbs/bootstrap/js/src/collapse.js b/vendor/twbs/bootstrap/js/src/collapse.js index af3163be9..fd85fbde2 100644 --- a/vendor/twbs/bootstrap/js/src/collapse.js +++ b/vendor/twbs/bootstrap/js/src/collapse.js @@ -1,12 +1,23 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): collapse.js + * Bootstrap (v5.0.1): collapse.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElement, + getSelectorFromElement, + getElementFromSelector, + reflow, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,11 +26,9 @@ import Util from './util' */ const NAME = 'collapse' -const VERSION = '4.6.0' const DATA_KEY = 'bs.collapse' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const Default = { toggle: true, @@ -42,11 +51,11 @@ const CLASS_NAME_COLLAPSE = 'collapse' const CLASS_NAME_COLLAPSING = 'collapsing' const CLASS_NAME_COLLAPSED = 'collapsed' -const DIMENSION_WIDTH = 'width' -const DIMENSION_HEIGHT = 'height' +const WIDTH = 'width' +const HEIGHT = 'height' const SELECTOR_ACTIVES = '.show, .collapsing' -const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]' /** * ------------------------------------------------------------------------ @@ -54,24 +63,26 @@ const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]' * ------------------------------------------------------------------------ */ -class Collapse { +class Collapse extends BaseComponent { constructor(element, config) { + super(element) + this._isTransitioning = false - this._element = element this._config = this._getConfig(config) - this._triggerArray = [].slice.call(document.querySelectorAll( - `[data-toggle="collapse"][href="#${element.id}"],` + - `[data-toggle="collapse"][data-target="#${element.id}"]` - )) + this._triggerArray = SelectorEngine.find( + `${SELECTOR_DATA_TOGGLE}[href="#${this._element.id}"],` + + `${SELECTOR_DATA_TOGGLE}[data-bs-target="#${this._element.id}"]` + ) + + const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE) - const toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)) for (let i = 0, len = toggleList.length; i < len; i++) { const elem = toggleList[i] - const selector = Util.getSelectorFromElement(elem) - const filterElement = [].slice.call(document.querySelectorAll(selector)) - .filter(foundElem => foundElem === element) + const selector = getSelectorFromElement(elem) + const filterElement = SelectorEngine.find(selector) + .filter(foundElem => foundElem === this._element) - if (selector !== null && filterElement.length > 0) { + if (selector !== null && filterElement.length) { this._selector = selector this._triggerArray.push(elem) } @@ -90,18 +101,18 @@ class Collapse { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public toggle() { - if ($(this._element).hasClass(CLASS_NAME_SHOW)) { + if (this._element.classList.contains(CLASS_NAME_SHOW)) { this.hide() } else { this.show() @@ -109,8 +120,7 @@ class Collapse { } show() { - if (this._isTransitioning || - $(this._element).hasClass(CLASS_NAME_SHOW)) { + if (this._isTransitioning || this._element.classList.contains(CLASS_NAME_SHOW)) { return } @@ -118,10 +128,10 @@ class Collapse { let activesData if (this._parent) { - actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES)) + actives = SelectorEngine.find(SELECTOR_ACTIVES, this._parent) .filter(elem => { if (typeof this._config.parent === 'string') { - return elem.getAttribute('data-parent') === this._config.parent + return elem.getAttribute('data-bs-parent') === this._config.parent } return elem.classList.contains(CLASS_NAME_COLLAPSE) @@ -132,74 +142,74 @@ class Collapse { } } + const container = SelectorEngine.findOne(this._selector) if (actives) { - activesData = $(actives).not(this._selector).data(DATA_KEY) + const tempActiveData = actives.find(elem => container !== elem) + activesData = tempActiveData ? Data.get(tempActiveData, DATA_KEY) : null + if (activesData && activesData._isTransitioning) { return } } - const startEvent = $.Event(EVENT_SHOW) - $(this._element).trigger(startEvent) - if (startEvent.isDefaultPrevented()) { + const startEvent = EventHandler.trigger(this._element, EVENT_SHOW) + if (startEvent.defaultPrevented) { return } if (actives) { - Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide') - if (!activesData) { - $(actives).data(DATA_KEY, null) - } + actives.forEach(elemActive => { + if (container !== elemActive) { + Collapse.collapseInterface(elemActive, 'hide') + } + + if (!activesData) { + Data.set(elemActive, DATA_KEY, null) + } + }) } const dimension = this._getDimension() - $(this._element) - .removeClass(CLASS_NAME_COLLAPSE) - .addClass(CLASS_NAME_COLLAPSING) + this._element.classList.remove(CLASS_NAME_COLLAPSE) + this._element.classList.add(CLASS_NAME_COLLAPSING) this._element.style[dimension] = 0 if (this._triggerArray.length) { - $(this._triggerArray) - .removeClass(CLASS_NAME_COLLAPSED) - .attr('aria-expanded', true) + this._triggerArray.forEach(element => { + element.classList.remove(CLASS_NAME_COLLAPSED) + element.setAttribute('aria-expanded', true) + }) } this.setTransitioning(true) const complete = () => { - $(this._element) - .removeClass(CLASS_NAME_COLLAPSING) - .addClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`) + this._element.classList.remove(CLASS_NAME_COLLAPSING) + this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW) this._element.style[dimension] = '' this.setTransitioning(false) - $(this._element).trigger(EVENT_SHOWN) + EventHandler.trigger(this._element, EVENT_SHOWN) } const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) const scrollSize = `scroll${capitalizedDimension}` - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - - $(this._element) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) + this._queueCallback(complete, this._element, true) this._element.style[dimension] = `${this._element[scrollSize]}px` } hide() { - if (this._isTransitioning || - !$(this._element).hasClass(CLASS_NAME_SHOW)) { + if (this._isTransitioning || !this._element.classList.contains(CLASS_NAME_SHOW)) { return } - const startEvent = $.Event(EVENT_HIDE) - $(this._element).trigger(startEvent) - if (startEvent.isDefaultPrevented()) { + const startEvent = EventHandler.trigger(this._element, EVENT_HIDE) + if (startEvent.defaultPrevented) { return } @@ -207,24 +217,20 @@ class Collapse { this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px` - Util.reflow(this._element) + reflow(this._element) - $(this._element) - .addClass(CLASS_NAME_COLLAPSING) - .removeClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`) + this._element.classList.add(CLASS_NAME_COLLAPSING) + this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW) const triggerArrayLength = this._triggerArray.length if (triggerArrayLength > 0) { for (let i = 0; i < triggerArrayLength; i++) { const trigger = this._triggerArray[i] - const selector = Util.getSelectorFromElement(trigger) + const elem = getElementFromSelector(trigger) - if (selector !== null) { - const $elem = $([].slice.call(document.querySelectorAll(selector))) - if (!$elem.hasClass(CLASS_NAME_SHOW)) { - $(trigger).addClass(CLASS_NAME_COLLAPSED) - .attr('aria-expanded', false) - } + if (elem && !elem.classList.contains(CLASS_NAME_SHOW)) { + trigger.classList.add(CLASS_NAME_COLLAPSED) + trigger.setAttribute('aria-expanded', false) } } } @@ -233,34 +239,20 @@ class Collapse { const complete = () => { this.setTransitioning(false) - $(this._element) - .removeClass(CLASS_NAME_COLLAPSING) - .addClass(CLASS_NAME_COLLAPSE) - .trigger(EVENT_HIDDEN) + this._element.classList.remove(CLASS_NAME_COLLAPSING) + this._element.classList.add(CLASS_NAME_COLLAPSE) + EventHandler.trigger(this._element, EVENT_HIDDEN) } this._element.style[dimension] = '' - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - $(this._element) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) + this._queueCallback(complete, this._element, true) } setTransitioning(isTransitioning) { this._isTransitioning = isTransitioning } - dispose() { - $.removeData(this._element, DATA_KEY) - - this._config = null - this._parent = null - this._element = null - this._triggerArray = null - this._isTransitioning = null - } - // Private _getConfig(config) { @@ -269,85 +261,82 @@ class Collapse { ...config } config.toggle = Boolean(config.toggle) // Coerce string values - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } _getDimension() { - const hasWidth = $(this._element).hasClass(DIMENSION_WIDTH) - return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT + return this._element.classList.contains(WIDTH) ? WIDTH : HEIGHT } _getParent() { - let parent + let { parent } = this._config - if (Util.isElement(this._config.parent)) { - parent = this._config.parent + parent = getElement(parent) - // It's a jQuery object - if (typeof this._config.parent.jquery !== 'undefined') { - parent = this._config.parent[0] - } - } else { - parent = document.querySelector(this._config.parent) - } + const selector = `${SELECTOR_DATA_TOGGLE}[data-bs-parent="${parent}"]` - const selector = `[data-toggle="collapse"][data-parent="${this._config.parent}"]` - const children = [].slice.call(parent.querySelectorAll(selector)) + SelectorEngine.find(selector, parent) + .forEach(element => { + const selected = getElementFromSelector(element) - $(children).each((i, element) => { - this._addAriaAndCollapsedClass( - Collapse._getTargetFromElement(element), - [element] - ) - }) + this._addAriaAndCollapsedClass( + selected, + [element] + ) + }) return parent } _addAriaAndCollapsedClass(element, triggerArray) { - const isOpen = $(element).hasClass(CLASS_NAME_SHOW) - - if (triggerArray.length) { - $(triggerArray) - .toggleClass(CLASS_NAME_COLLAPSED, !isOpen) - .attr('aria-expanded', isOpen) + if (!element || !triggerArray.length) { + return } + + const isOpen = element.classList.contains(CLASS_NAME_SHOW) + + triggerArray.forEach(elem => { + if (isOpen) { + elem.classList.remove(CLASS_NAME_COLLAPSED) + } else { + elem.classList.add(CLASS_NAME_COLLAPSED) + } + + elem.setAttribute('aria-expanded', isOpen) + }) } // Static - static _getTargetFromElement(element) { - const selector = Util.getSelectorFromElement(element) - return selector ? document.querySelector(selector) : null - } + static collapseInterface(element, config) { + let data = Data.get(element, DATA_KEY) + const _config = { + ...Default, + ...Manipulator.getDataAttributes(element), + ...(typeof config === 'object' && config ? config : {}) + } - static _jQueryInterface(config) { - return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) - const _config = { - ...Default, - ...$element.data(), - ...(typeof config === 'object' && config ? config : {}) - } + if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false + } - if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { - _config.toggle = false - } + if (!data) { + data = new Collapse(element, _config) + } - if (!data) { - data = new Collapse(this, _config) - $element.data(DATA_KEY, data) + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } + data[config]() + } + } - data[config]() - } + static jQueryInterface(config) { + return this.each(function () { + Collapse.collapseInterface(this, config) }) } } @@ -358,21 +347,32 @@ class Collapse { * ------------------------------------------------------------------------ */ -$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { // preventDefault only for elements (which change the URL) not inside the collapsible element - if (event.currentTarget.tagName === 'A') { + if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) { event.preventDefault() } - const $trigger = $(this) - const selector = Util.getSelectorFromElement(this) - const selectors = [].slice.call(document.querySelectorAll(selector)) + const triggerData = Manipulator.getDataAttributes(this) + const selector = getSelectorFromElement(this) + const selectorElements = SelectorEngine.find(selector) + + selectorElements.forEach(element => { + const data = Data.get(element, DATA_KEY) + let config + if (data) { + // update parent attribute + if (data._parent === null && typeof triggerData.parent === 'string') { + data._config.parent = triggerData.parent + data._parent = data._getParent() + } - $(selectors).each(function () { - const $target = $(this) - const data = $target.data(DATA_KEY) - const config = data ? 'toggle' : $trigger.data() - Collapse._jQueryInterface.call($target, config) + config = 'toggle' + } else { + config = triggerData + } + + Collapse.collapseInterface(element, config) }) }) @@ -380,13 +380,9 @@ $(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Collapse to jQuery only if jQuery is present */ -$.fn[NAME] = Collapse._jQueryInterface -$.fn[NAME].Constructor = Collapse -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Collapse._jQueryInterface -} +defineJQueryPlugin(Collapse) export default Collapse diff --git a/vendor/twbs/bootstrap/js/src/dropdown.js b/vendor/twbs/bootstrap/js/src/dropdown.js index 76dda8f57..565edb892 100644 --- a/vendor/twbs/bootstrap/js/src/dropdown.js +++ b/vendor/twbs/bootstrap/js/src/dropdown.js @@ -1,13 +1,28 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): dropdown.js + * Bootstrap (v5.0.1): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Popper from 'popper.js' -import Util from './util' +import * as Popper from '@popperjs/core' + +import { + defineJQueryPlugin, + getElement, + getElementFromSelector, + isDisabled, + isElement, + isVisible, + isRTL, + noop, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -16,18 +31,18 @@ import Util from './util' */ const NAME = 'dropdown' -const VERSION = '4.6.0' const DATA_KEY = 'bs.dropdown' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key -const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key -const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key -const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key -const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key -const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) -const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) + +const ESCAPE_KEY = 'Escape' +const SPACE_KEY = 'Space' +const TAB_KEY = 'Tab' +const ARROW_UP_KEY = 'ArrowUp' +const ARROW_DOWN_KEY = 'ArrowDown' +const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button + +const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`) const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` @@ -38,43 +53,40 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}` const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}` -const CLASS_NAME_DISABLED = 'disabled' const CLASS_NAME_SHOW = 'show' const CLASS_NAME_DROPUP = 'dropup' -const CLASS_NAME_DROPRIGHT = 'dropright' -const CLASS_NAME_DROPLEFT = 'dropleft' -const CLASS_NAME_MENURIGHT = 'dropdown-menu-right' -const CLASS_NAME_POSITION_STATIC = 'position-static' +const CLASS_NAME_DROPEND = 'dropend' +const CLASS_NAME_DROPSTART = 'dropstart' +const CLASS_NAME_NAVBAR = 'navbar' -const SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]' -const SELECTOR_FORM_CHILD = '.dropdown form' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]' const SELECTOR_MENU = '.dropdown-menu' const SELECTOR_NAVBAR_NAV = '.navbar-nav' const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' -const PLACEMENT_TOP = 'top-start' -const PLACEMENT_TOPEND = 'top-end' -const PLACEMENT_BOTTOM = 'bottom-start' -const PLACEMENT_BOTTOMEND = 'bottom-end' -const PLACEMENT_RIGHT = 'right-start' -const PLACEMENT_LEFT = 'left-start' +const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start' +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 = { - offset: 0, - flip: true, - boundary: 'scrollParent', + offset: [0, 2], + boundary: 'clippingParents', reference: 'toggle', display: 'dynamic', - popperConfig: null + popperConfig: null, + autoClose: true } const DefaultType = { - offset: '(number|string|function)', - flip: 'boolean', + offset: '(array|string|function)', boundary: '(string|element)', - reference: '(string|element)', + reference: '(string|element|object)', display: 'string', - popperConfig: '(null|object)' + popperConfig: '(null|object|function)', + autoClose: '(boolean|string)' } /** @@ -83,9 +95,10 @@ const DefaultType = { * ------------------------------------------------------------------------ */ -class Dropdown { +class Dropdown extends BaseComponent { constructor(element, config) { - this._element = element + super(element) + this._popper = null this._config = this._getConfig(config) this._menu = this._getMenuElement() @@ -96,10 +109,6 @@ class Dropdown { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } @@ -108,47 +117,47 @@ class Dropdown { return DefaultType } + static get NAME() { + return NAME + } + // Public toggle() { - if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED)) { + if (isDisabled(this._element)) { return } - const isActive = $(this._menu).hasClass(CLASS_NAME_SHOW) - - Dropdown._clearMenus() + const isActive = this._element.classList.contains(CLASS_NAME_SHOW) if (isActive) { + this.hide() return } - this.show(true) + this.show() } - show(usePopper = false) { - if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || $(this._menu).hasClass(CLASS_NAME_SHOW)) { + show() { + if (isDisabled(this._element) || this._menu.classList.contains(CLASS_NAME_SHOW)) { return } + const parent = Dropdown.getParentFromElement(this._element) const relatedTarget = { relatedTarget: this._element } - const showEvent = $.Event(EVENT_SHOW, relatedTarget) - const parent = Dropdown._getParentFromElement(this._element) - $(parent).trigger(showEvent) + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget) - if (showEvent.isDefaultPrevented()) { + if (showEvent.defaultPrevented) { return } // Totally disable Popper for Dropdowns in Navbar - if (!this._inNavbar && usePopper) { - /** - * Check for Popper dependency - * Popper - https://popper.js.org - */ + if (this._inNavbar) { + Manipulator.setDataAttribute(this._menu, 'popper', 'none') + } else { if (typeof Popper === 'undefined') { throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)') } @@ -157,23 +166,20 @@ class Dropdown { if (this._config.reference === 'parent') { referenceElement = parent - } else if (Util.isElement(this._config.reference)) { + } else if (isElement(this._config.reference)) { + referenceElement = getElement(this._config.reference) + } else if (typeof this._config.reference === 'object') { referenceElement = this._config.reference - - // Check if it's jQuery element - if (typeof this._config.reference.jquery !== 'undefined') { - referenceElement = this._config.reference[0] - } } - // If boundary is not `scrollParent`, then set position to `static` - // to allow the menu to "escape" the scroll parent's boundaries - // https://github.com/twbs/bootstrap/issues/24251 - if (this._config.boundary !== 'scrollParent') { - $(parent).addClass(CLASS_NAME_POSITION_STATIC) - } + const popperConfig = this._getPopperConfig() + const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false) - this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()) + this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig) + + if (isDisplayStatic) { + Manipulator.setDataAttribute(this._menu, 'popper', 'static') + } } // If this is a touch-enabled device we add extra @@ -181,266 +187,273 @@ class Dropdown { // 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).length === 0) { - $(document.body).children().on('mouseover', null, $.noop) + !parent.closest(SELECTOR_NAVBAR_NAV)) { + [].concat(...document.body.children) + .forEach(elem => EventHandler.on(elem, 'mouseover', noop)) } this._element.focus() this._element.setAttribute('aria-expanded', true) - $(this._menu).toggleClass(CLASS_NAME_SHOW) - $(parent) - .toggleClass(CLASS_NAME_SHOW) - .trigger($.Event(EVENT_SHOWN, relatedTarget)) + this._menu.classList.toggle(CLASS_NAME_SHOW) + this._element.classList.toggle(CLASS_NAME_SHOW) + EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget) } hide() { - if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || !$(this._menu).hasClass(CLASS_NAME_SHOW)) { + if (isDisabled(this._element) || !this._menu.classList.contains(CLASS_NAME_SHOW)) { return } const relatedTarget = { relatedTarget: this._element } - const hideEvent = $.Event(EVENT_HIDE, relatedTarget) - const parent = Dropdown._getParentFromElement(this._element) - - $(parent).trigger(hideEvent) - if (hideEvent.isDefaultPrevented()) { - return - } - - if (this._popper) { - this._popper.destroy() - } - - $(this._menu).toggleClass(CLASS_NAME_SHOW) - $(parent) - .toggleClass(CLASS_NAME_SHOW) - .trigger($.Event(EVENT_HIDDEN, relatedTarget)) + this._completeHide(relatedTarget) } dispose() { - $.removeData(this._element, DATA_KEY) - $(this._element).off(EVENT_KEY) - this._element = null - this._menu = null - if (this._popper !== null) { + if (this._popper) { this._popper.destroy() - this._popper = null } + + super.dispose() } update() { this._inNavbar = this._detectNavbar() - if (this._popper !== null) { - this._popper.scheduleUpdate() + if (this._popper) { + this._popper.update() } } // Private _addEventListeners() { - $(this._element).on(EVENT_CLICK, event => { + EventHandler.on(this._element, EVENT_CLICK, event => { event.preventDefault() - event.stopPropagation() this.toggle() }) } + _completeHide(relatedTarget) { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, 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) + this._element.classList.remove(CLASS_NAME_SHOW) + this._element.setAttribute('aria-expanded', 'false') + Manipulator.removeDataAttribute(this._menu, 'popper') + EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget) + } + _getConfig(config) { config = { ...this.constructor.Default, - ...$(this._element).data(), + ...Manipulator.getDataAttributes(this._element), ...config } - Util.typeCheckConfig( - NAME, - config, - this.constructor.DefaultType - ) + typeCheckConfig(NAME, 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.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`) + } return config } _getMenuElement() { - if (!this._menu) { - const parent = Dropdown._getParentFromElement(this._element) + return SelectorEngine.next(this._element, SELECTOR_MENU)[0] + } - if (parent) { - this._menu = parent.querySelector(SELECTOR_MENU) - } + _getPlacement() { + const parentDropdown = this._element.parentNode + + if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { + return PLACEMENT_RIGHT } - return this._menu - } + if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { + return PLACEMENT_LEFT + } - _getPlacement() { - const $parentDropdown = $(this._element.parentNode) - let placement = PLACEMENT_BOTTOM - - // Handle dropup - if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) { - placement = $(this._menu).hasClass(CLASS_NAME_MENURIGHT) ? - PLACEMENT_TOPEND : - PLACEMENT_TOP - } else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) { - placement = PLACEMENT_RIGHT - } else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) { - placement = PLACEMENT_LEFT - } else if ($(this._menu).hasClass(CLASS_NAME_MENURIGHT)) { - placement = PLACEMENT_BOTTOMEND - } - - return placement + // We need to trim the value because custom properties can also include spaces + const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end' + + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { + return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP + } + + return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM } _detectNavbar() { - return $(this._element).closest('.navbar').length > 0 + return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null } _getOffset() { - const offset = {} + const { offset } = this._config - if (typeof this._config.offset === 'function') { - offset.fn = data => { - data.offsets = { - ...data.offsets, - ...(this._config.offset(data.offsets, this._element) || {}) - } + if (typeof offset === 'string') { + return offset.split(',').map(val => Number.parseInt(val, 10)) + } - return data - } - } else { - offset.offset = this._config.offset + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element) } return offset } _getPopperConfig() { - const popperConfig = { + const defaultBsPopperConfig = { placement: this._getPlacement(), - modifiers: { - offset: this._getOffset(), - flip: { - enabled: this._config.flip - }, - preventOverflow: { - boundariesElement: this._config.boundary + modifiers: [{ + name: 'preventOverflow', + options: { + boundary: this._config.boundary } - } + }, + { + name: 'offset', + options: { + offset: this._getOffset() + } + }] } // Disable Popper if we have a static display if (this._config.display === 'static') { - popperConfig.modifiers.applyStyle = { + defaultBsPopperConfig.modifiers = [{ + name: 'applyStyles', enabled: false - } + }] } return { - ...popperConfig, - ...this._config.popperConfig + ...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 _jQueryInterface(config) { - return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = typeof config === 'object' ? config : null + static dropdownInterface(element, config) { + let data = Data.get(element, DATA_KEY) + const _config = typeof config === 'object' ? config : null + + if (!data) { + data = new Dropdown(element, _config) + } - if (!data) { - data = new Dropdown(this, _config) - $(this).data(DATA_KEY, data) + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } + data[config]() + } + } - data[config]() - } + static jQueryInterface(config) { + return this.each(function () { + Dropdown.dropdownInterface(this, config) }) } - static _clearMenus(event) { - if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || - event.type === 'keyup' && event.which !== TAB_KEYCODE)) { + static clearMenus(event) { + if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) { return } - const toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)) + const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE) for (let i = 0, len = toggles.length; i < len; i++) { - const parent = Dropdown._getParentFromElement(toggles[i]) - const context = $(toggles[i]).data(DATA_KEY) - const relatedTarget = { - relatedTarget: toggles[i] - } - - if (event && event.type === 'click') { - relatedTarget.clickEvent = event - } - - if (!context) { - continue - } - - const dropdownMenu = context._menu - if (!$(parent).hasClass(CLASS_NAME_SHOW)) { + const context = Data.get(toggles[i], DATA_KEY) + if (!context || context._config.autoClose === false) { continue } - if (event && (event.type === 'click' && - /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && - $.contains(parent, event.target)) { + if (!context._element.classList.contains(CLASS_NAME_SHOW)) { continue } - const hideEvent = $.Event(EVENT_HIDE, relatedTarget) - $(parent).trigger(hideEvent) - if (hideEvent.isDefaultPrevented()) { - continue + const relatedTarget = { + relatedTarget: context._element } - // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - if ('ontouchstart' in document.documentElement) { - $(document.body).children().off('mouseover', null, $.noop) - } + if (event) { + 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 + } - toggles[i].setAttribute('aria-expanded', 'false') + // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu + if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) { + continue + } - if (context._popper) { - context._popper.destroy() + if (event.type === 'click') { + relatedTarget.clickEvent = event + } } - $(dropdownMenu).removeClass(CLASS_NAME_SHOW) - $(parent) - .removeClass(CLASS_NAME_SHOW) - .trigger($.Event(EVENT_HIDDEN, relatedTarget)) + context._completeHide(relatedTarget) } } - static _getParentFromElement(element) { - let parent - const selector = Util.getSelectorFromElement(element) - - if (selector) { - parent = document.querySelector(selector) - } - - return parent || element.parentNode + static getParentFromElement(element) { + return getElementFromSelector(element) || element.parentNode } - // eslint-disable-next-line complexity - static _dataApiKeydownHandler(event) { + static dataApiKeydownHandler(event) { // If not input/textarea: // - And not a key in REGEXP_KEYDOWN => not a dropdown command // If input/textarea: @@ -449,57 +462,45 @@ class Dropdown { // - If key is not up or down => not a dropdown command // - If trigger inside the menu => not a dropdown command if (/input|textarea/i.test(event.target.tagName) ? - event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && - (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || - $(event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { - return - } - - if (this.disabled || $(this).hasClass(CLASS_NAME_DISABLED)) { + event.key === SPACE_KEY || (event.key !== ESCAPE_KEY && + ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) || + event.target.closest(SELECTOR_MENU))) : + !REGEXP_KEYDOWN.test(event.key)) { return } - const parent = Dropdown._getParentFromElement(this) - const isActive = $(parent).hasClass(CLASS_NAME_SHOW) + const isActive = this.classList.contains(CLASS_NAME_SHOW) - if (!isActive && event.which === ESCAPE_KEYCODE) { + if (!isActive && event.key === ESCAPE_KEY) { return } event.preventDefault() event.stopPropagation() - if (!isActive || (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { - if (event.which === ESCAPE_KEYCODE) { - $(parent.querySelector(SELECTOR_DATA_TOGGLE)).trigger('focus') - } - - $(this).trigger('click') + if (isDisabled(this)) { return } - const items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)) - .filter(item => $(item).is(':visible')) + const getToggleButton = () => this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] - if (items.length === 0) { + if (event.key === ESCAPE_KEY) { + getToggleButton().focus() + Dropdown.clearMenus() return } - let index = items.indexOf(event.target) - - if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up - index-- - } - - if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down - index++ + if (!isActive && (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY)) { + getToggleButton().click() + return } - if (index < 0) { - index = 0 + if (!isActive || event.key === SPACE_KEY) { + Dropdown.clearMenus() + return } - items[index].focus() + Dropdown.getInstance(getToggleButton())._selectMenuItem(event) } } @@ -509,30 +510,22 @@ class Dropdown { * ------------------------------------------------------------------------ */ -$(document) - .on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown._dataApiKeydownHandler) - .on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler) - .on(`${EVENT_CLICK_DATA_API} ${EVENT_KEYUP_DATA_API}`, Dropdown._clearMenus) - .on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - event.preventDefault() - event.stopPropagation() - Dropdown._jQueryInterface.call($(this), 'toggle') - }) - .on(EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => { - e.stopPropagation() - }) +EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler) +EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler) +EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus) +EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { + event.preventDefault() + Dropdown.dropdownInterface(this) +}) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Dropdown to jQuery only if jQuery is present */ -$.fn[NAME] = Dropdown._jQueryInterface -$.fn[NAME].Constructor = Dropdown -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Dropdown._jQueryInterface -} +defineJQueryPlugin(Dropdown) export default Dropdown diff --git a/vendor/twbs/bootstrap/js/src/modal.js b/vendor/twbs/bootstrap/js/src/modal.js index 2e3017024..d4436bf4f 100644 --- a/vendor/twbs/bootstrap/js/src/modal.js +++ b/vendor/twbs/bootstrap/js/src/modal.js @@ -1,12 +1,26 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): modal.js + * Bootstrap (v5.0.1): modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + emulateTransitionEnd, + getElementFromSelector, + getTransitionDurationFromElement, + isRTL, + isVisible, + reflow, + typeCheckConfig +} from './util/index' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import { getWidth as getScrollBarWidth, hide as scrollBarHide, reset as scrollBarReset } from './util/scrollbar' +import BaseComponent from './base-component' +import Backdrop from './util/backdrop' /** * ------------------------------------------------------------------------ @@ -15,25 +29,21 @@ import Util from './util' */ const NAME = 'modal' -const VERSION = '4.6.0' const DATA_KEY = 'bs.modal' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key +const ESCAPE_KEY = 'Escape' const Default = { backdrop: true, keyboard: true, - focus: true, - show: true + focus: true } const DefaultType = { backdrop: '(boolean|string)', keyboard: 'boolean', - focus: 'boolean', - show: 'boolean' + focus: 'boolean' } const EVENT_HIDE = `hide${EVENT_KEY}` @@ -49,9 +59,6 @@ const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}` const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` -const CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable' -const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure' -const CLASS_NAME_BACKDROP = 'modal-backdrop' const CLASS_NAME_OPEN = 'modal-open' const CLASS_NAME_FADE = 'fade' const CLASS_NAME_SHOW = 'show' @@ -59,10 +66,8 @@ const CLASS_NAME_STATIC = 'modal-static' const SELECTOR_DIALOG = '.modal-dialog' const SELECTOR_MODAL_BODY = '.modal-body' -const SELECTOR_DATA_TOGGLE = '[data-toggle="modal"]' -const SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]' -const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' -const SELECTOR_STICKY_CONTENT = '.sticky-top' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]' +const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="modal"]' /** * ------------------------------------------------------------------------ @@ -70,29 +75,28 @@ const SELECTOR_STICKY_CONTENT = '.sticky-top' * ------------------------------------------------------------------------ */ -class Modal { +class Modal extends BaseComponent { constructor(element, config) { + super(element) + this._config = this._getConfig(config) - this._element = element - this._dialog = element.querySelector(SELECTOR_DIALOG) - this._backdrop = null + this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element) + this._backdrop = this._initializeBackDrop() this._isShown = false - this._isBodyOverflowing = false this._ignoreBackdropClick = false this._isTransitioning = false - this._scrollbarWidth = 0 } // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public toggle(relatedTarget) { @@ -104,39 +108,34 @@ class Modal { return } - if ($(this._element).hasClass(CLASS_NAME_FADE)) { + if (this._isAnimated()) { this._isTransitioning = true } - const showEvent = $.Event(EVENT_SHOW, { + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget }) - $(this._element).trigger(showEvent) - - if (this._isShown || showEvent.isDefaultPrevented()) { + if (this._isShown || showEvent.defaultPrevented) { return } this._isShown = true - this._checkScrollbar() - this._setScrollbar() + scrollBarHide() + + document.body.classList.add(CLASS_NAME_OPEN) this._adjustDialog() this._setEscapeEvent() this._setResizeEvent() - $(this._element).on( - EVENT_CLICK_DISMISS, - SELECTOR_DATA_DISMISS, - event => this.hide(event) - ) + EventHandler.on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, event => this.hide(event)) - $(this._dialog).on(EVENT_MOUSEDOWN_DISMISS, () => { - $(this._element).one(EVENT_MOUSEUP_DISMISS, event => { - if ($(event.target).is(this._element)) { + EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => { + EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => { + if (event.target === this._element) { this._ignoreBackdropClick = true } }) @@ -154,64 +153,45 @@ class Modal { return } - const hideEvent = $.Event(EVENT_HIDE) - - $(this._element).trigger(hideEvent) + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE) - if (!this._isShown || hideEvent.isDefaultPrevented()) { + if (hideEvent.defaultPrevented) { return } this._isShown = false - const transition = $(this._element).hasClass(CLASS_NAME_FADE) + const isAnimated = this._isAnimated() - if (transition) { + if (isAnimated) { this._isTransitioning = true } this._setEscapeEvent() this._setResizeEvent() - $(document).off(EVENT_FOCUSIN) + EventHandler.off(document, EVENT_FOCUSIN) - $(this._element).removeClass(CLASS_NAME_SHOW) + this._element.classList.remove(CLASS_NAME_SHOW) - $(this._element).off(EVENT_CLICK_DISMISS) - $(this._dialog).off(EVENT_MOUSEDOWN_DISMISS) + EventHandler.off(this._element, EVENT_CLICK_DISMISS) + EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS) - if (transition) { - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - - $(this._element) - .one(Util.TRANSITION_END, event => this._hideModal(event)) - .emulateTransitionEnd(transitionDuration) - } else { - this._hideModal() - } + this._queueCallback(() => this._hideModal(), this._element, isAnimated) } dispose() { - [window, this._element, this._dialog] - .forEach(htmlElement => $(htmlElement).off(EVENT_KEY)) + [window, this._dialog] + .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY)) + + this._backdrop.dispose() + 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 */ - $(document).off(EVENT_FOCUSIN) - - $.removeData(this._element, DATA_KEY) - - this._config = null - this._element = null - this._dialog = null - this._backdrop = null - this._isShown = null - this._isBodyOverflowing = null - this._ignoreBackdropClick = null - this._isTransitioning = null - this._scrollbarWidth = null + EventHandler.off(document, EVENT_FOCUSIN) } handleUpdate() { @@ -220,53 +200,28 @@ class Modal { // 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, + ...Manipulator.getDataAttributes(this._element), ...config } - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } - _triggerBackdropTransition() { - const hideEventPrevented = $.Event(EVENT_HIDE_PREVENTED) - - $(this._element).trigger(hideEventPrevented) - if (hideEventPrevented.isDefaultPrevented()) { - return - } - - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - - if (!isModalOverflowing) { - this._element.style.overflowY = 'hidden' - } - - this._element.classList.add(CLASS_NAME_STATIC) - - const modalTransitionDuration = Util.getTransitionDurationFromElement(this._dialog) - $(this._element).off(Util.TRANSITION_END) - - $(this._element).one(Util.TRANSITION_END, () => { - this._element.classList.remove(CLASS_NAME_STATIC) - if (!isModalOverflowing) { - $(this._element).one(Util.TRANSITION_END, () => { - this._element.style.overflowY = '' - }) - .emulateTransitionEnd(this._element, modalTransitionDuration) - } - }) - .emulateTransitionEnd(modalTransitionDuration) - this._element.focus() - } - _showElement(relatedTarget) { - const transition = $(this._element).hasClass(CLASS_NAME_FADE) - const modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null + const isAnimated = this._isAnimated() + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) - if (!this._element.parentNode || - this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { + if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { // Don't move modal's DOM position document.body.appendChild(this._element) } @@ -275,79 +230,67 @@ class Modal { this._element.removeAttribute('aria-hidden') this._element.setAttribute('aria-modal', true) this._element.setAttribute('role', 'dialog') + this._element.scrollTop = 0 - if ($(this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) { + if (modalBody) { modalBody.scrollTop = 0 - } else { - this._element.scrollTop = 0 } - if (transition) { - Util.reflow(this._element) + if (isAnimated) { + reflow(this._element) } - $(this._element).addClass(CLASS_NAME_SHOW) + this._element.classList.add(CLASS_NAME_SHOW) if (this._config.focus) { this._enforceFocus() } - const shownEvent = $.Event(EVENT_SHOWN, { - relatedTarget - }) - const transitionComplete = () => { if (this._config.focus) { this._element.focus() } this._isTransitioning = false - $(this._element).trigger(shownEvent) + EventHandler.trigger(this._element, EVENT_SHOWN, { + relatedTarget + }) } - if (transition) { - const transitionDuration = Util.getTransitionDurationFromElement(this._dialog) - - $(this._dialog) - .one(Util.TRANSITION_END, transitionComplete) - .emulateTransitionEnd(transitionDuration) - } else { - transitionComplete() - } + this._queueCallback(transitionComplete, this._dialog, isAnimated) } _enforceFocus() { - $(document) - .off(EVENT_FOCUSIN) // Guard against infinite focus loop - .on(EVENT_FOCUSIN, event => { - if (document !== event.target && - this._element !== event.target && - $(this._element).has(event.target).length === 0) { - this._element.focus() - } - }) + EventHandler.off(document, EVENT_FOCUSIN) // guard against infinite focus loop + EventHandler.on(document, EVENT_FOCUSIN, event => { + if (document !== event.target && + this._element !== event.target && + !this._element.contains(event.target)) { + this._element.focus() + } + }) } _setEscapeEvent() { if (this._isShown) { - $(this._element).on(EVENT_KEYDOWN_DISMISS, event => { - if (this._config.keyboard && event.which === ESCAPE_KEYCODE) { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + if (this._config.keyboard && event.key === ESCAPE_KEY) { event.preventDefault() this.hide() - } else if (!this._config.keyboard && event.which === ESCAPE_KEYCODE) { + } else if (!this._config.keyboard && event.key === ESCAPE_KEY) { this._triggerBackdropTransition() } }) - } else if (!this._isShown) { - $(this._element).off(EVENT_KEYDOWN_DISMISS) + } else { + EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS) } } _setResizeEvent() { if (this._isShown) { - $(window).on(EVENT_RESIZE, event => this.handleUpdate(event)) + EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog()) } else { - $(window).off(EVENT_RESIZE) + EventHandler.off(window, EVENT_RESIZE) } } @@ -357,110 +300,82 @@ class Modal { this._element.removeAttribute('aria-modal') this._element.removeAttribute('role') this._isTransitioning = false - this._showBackdrop(() => { - $(document.body).removeClass(CLASS_NAME_OPEN) + this._backdrop.hide(() => { + document.body.classList.remove(CLASS_NAME_OPEN) this._resetAdjustments() - this._resetScrollbar() - $(this._element).trigger(EVENT_HIDDEN) + scrollBarReset() + EventHandler.trigger(this._element, EVENT_HIDDEN) }) } - _removeBackdrop() { - if (this._backdrop) { - $(this._backdrop).remove() - this._backdrop = null - } - } - _showBackdrop(callback) { - const animate = $(this._element).hasClass(CLASS_NAME_FADE) ? - CLASS_NAME_FADE : '' - - if (this._isShown && this._config.backdrop) { - this._backdrop = document.createElement('div') - this._backdrop.className = CLASS_NAME_BACKDROP - - if (animate) { - this._backdrop.classList.add(animate) - } - - $(this._backdrop).appendTo(document.body) - - $(this._element).on(EVENT_CLICK_DISMISS, event => { - if (this._ignoreBackdropClick) { - this._ignoreBackdropClick = false - return - } - - if (event.target !== event.currentTarget) { - return - } - - if (this._config.backdrop === 'static') { - this._triggerBackdropTransition() - } else { - this.hide() - } - }) - - if (animate) { - Util.reflow(this._backdrop) + EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => { + if (this._ignoreBackdropClick) { + this._ignoreBackdropClick = false + return } - $(this._backdrop).addClass(CLASS_NAME_SHOW) - - if (!callback) { + if (event.target !== event.currentTarget) { return } - if (!animate) { - callback() - return + if (this._config.backdrop === true) { + this.hide() + } else if (this._config.backdrop === 'static') { + this._triggerBackdropTransition() } + }) - const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop) + this._backdrop.show(callback) + } - $(this._backdrop) - .one(Util.TRANSITION_END, callback) - .emulateTransitionEnd(backdropTransitionDuration) - } else if (!this._isShown && this._backdrop) { - $(this._backdrop).removeClass(CLASS_NAME_SHOW) + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_FADE) + } - const callbackRemove = () => { - this._removeBackdrop() - if (callback) { - callback() - } - } + _triggerBackdropTransition() { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED) + if (hideEvent.defaultPrevented) { + return + } - if ($(this._element).hasClass(CLASS_NAME_FADE)) { - const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop) + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - $(this._backdrop) - .one(Util.TRANSITION_END, callbackRemove) - .emulateTransitionEnd(backdropTransitionDuration) - } else { - callbackRemove() - } - } else if (callback) { - callback() + if (!isModalOverflowing) { + this._element.style.overflowY = 'hidden' } + + this._element.classList.add(CLASS_NAME_STATIC) + const modalTransitionDuration = getTransitionDurationFromElement(this._dialog) + EventHandler.off(this._element, 'transitionend') + EventHandler.one(this._element, 'transitionend', () => { + this._element.classList.remove(CLASS_NAME_STATIC) + if (!isModalOverflowing) { + EventHandler.one(this._element, 'transitionend', () => { + this._element.style.overflowY = '' + }) + emulateTransitionEnd(this._element, modalTransitionDuration) + } + }) + emulateTransitionEnd(this._element, modalTransitionDuration) + this._element.focus() } // ---------------------------------------------------------------------- // the following methods are used to handle overflowing modals - // todo (fat): these should probably be refactored out of modal.js // ---------------------------------------------------------------------- _adjustDialog() { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight + const scrollbarWidth = getScrollBarWidth() + const isBodyOverflowing = scrollbarWidth > 0 - if (!this._isBodyOverflowing && isModalOverflowing) { - this._element.style.paddingLeft = `${this._scrollbarWidth}px` + if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) { + this._element.style.paddingLeft = `${scrollbarWidth}px` } - if (this._isBodyOverflowing && !isModalOverflowing) { - this._element.style.paddingRight = `${this._scrollbarWidth}px` + if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) { + this._element.style.paddingRight = `${scrollbarWidth}px` } } @@ -469,106 +384,21 @@ class Modal { 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) { - // Note: DOMNode.style.paddingRight returns the actual value or '' if not set - // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set - const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)) - const stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT)) - - // Adjust fixed content padding - $(fixedContent).each((index, element) => { - const actualPadding = element.style.paddingRight - const calculatedPadding = $(element).css('padding-right') - $(element) - .data('padding-right', actualPadding) - .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`) - }) - - // Adjust sticky content margin - $(stickyContent).each((index, element) => { - const actualMargin = element.style.marginRight - const calculatedMargin = $(element).css('margin-right') - $(element) - .data('margin-right', actualMargin) - .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`) - }) - - // Adjust body padding - const actualPadding = document.body.style.paddingRight - const calculatedPadding = $(document.body).css('padding-right') - $(document.body) - .data('padding-right', actualPadding) - .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`) - } - - $(document.body).addClass(CLASS_NAME_OPEN) - } - - _resetScrollbar() { - // Restore fixed content padding - const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)) - $(fixedContent).each((index, element) => { - const padding = $(element).data('padding-right') - $(element).removeData('padding-right') - element.style.paddingRight = padding ? padding : '' - }) - - // Restore sticky content - const elements = [].slice.call(document.querySelectorAll(`${SELECTOR_STICKY_CONTENT}`)) - $(elements).each((index, element) => { - const margin = $(element).data('margin-right') - if (typeof margin !== 'undefined') { - $(element).css('margin-right', margin).removeData('margin-right') - } - }) - - // Restore body padding - const padding = $(document.body).data('padding-right') - $(document.body).removeData('padding-right') - document.body.style.paddingRight = padding ? padding : '' - } - - _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) { + static jQueryInterface(config, relatedTarget) { return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = { - ...Default, - ...$(this).data(), - ...(typeof config === 'object' && config ? config : {}) - } + const data = Modal.getInstance(this) || new Modal(this, typeof config === 'object' ? config : {}) - if (!data) { - data = new Modal(this, _config) - $(this).data(DATA_KEY, data) + if (typeof config !== 'string') { + return } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } - - data[config](relatedTarget) - } else if (_config.show) { - data.show(relatedTarget) + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } + + data[config](relatedTarget) }) } } @@ -579,51 +409,38 @@ class Modal { * ------------------------------------------------------------------------ */ -$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - let target - const selector = Util.getSelectorFromElement(this) - - if (selector) { - target = document.querySelector(selector) - } +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { + const target = getElementFromSelector(this) - const config = $(target).data(DATA_KEY) ? - 'toggle' : { - ...$(target).data(), - ...$(this).data() - } - - if (this.tagName === 'A' || this.tagName === 'AREA') { + if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault() } - const $target = $(target).one(EVENT_SHOW, showEvent => { - if (showEvent.isDefaultPrevented()) { - // Only register focus restorer if modal will actually get shown + EventHandler.one(target, EVENT_SHOW, showEvent => { + if (showEvent.defaultPrevented) { + // only register focus restorer if modal will actually get shown return } - $target.one(EVENT_HIDDEN, () => { - if ($(this).is(':visible')) { + EventHandler.one(target, EVENT_HIDDEN, () => { + if (isVisible(this)) { this.focus() } }) }) - Modal._jQueryInterface.call($(target), config, this) + const data = Modal.getInstance(target) || new Modal(target) + + data.toggle(this) }) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Modal to jQuery only if jQuery is present */ -$.fn[NAME] = Modal._jQueryInterface -$.fn[NAME].Constructor = Modal -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Modal._jQueryInterface -} +defineJQueryPlugin(Modal) export default Modal diff --git a/vendor/twbs/bootstrap/js/src/popover.js b/vendor/twbs/bootstrap/js/src/popover.js index 4e2c260b9..b39985124 100644 --- a/vendor/twbs/bootstrap/js/src/popover.js +++ b/vendor/twbs/bootstrap/js/src/popover.js @@ -1,11 +1,13 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): popover.js + * Bootstrap (v5.0.1): popover.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import { defineJQueryPlugin } from './util/index' +import Data from './dom/data' +import SelectorEngine from './dom/selector-engine' import Tooltip from './tooltip' /** @@ -15,22 +17,22 @@ import Tooltip from './tooltip' */ const NAME = 'popover' -const VERSION = '4.6.0' const DATA_KEY = 'bs.popover' const EVENT_KEY = `.${DATA_KEY}` -const JQUERY_NO_CONFLICT = $.fn[NAME] const CLASS_PREFIX = 'bs-popover' const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') const Default = { ...Tooltip.Default, placement: 'right', + offset: [0, 8], trigger: 'click', content: '', template: '' + '
' + + '

' + + '
' + + '' } const DefaultType = { @@ -38,12 +40,6 @@ const DefaultType = { content: '(string|element|function)' } -const CLASS_NAME_FADE = 'fade' -const CLASS_NAME_SHOW = 'show' - -const SELECTOR_TITLE = '.popover-header' -const SELECTOR_CONTENT = '.popover-body' - const Event = { HIDE: `hide${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`, @@ -57,6 +53,12 @@ const Event = { MOUSELEAVE: `mouseleave${EVENT_KEY}` } +const CLASS_NAME_FADE = 'fade' +const CLASS_NAME_SHOW = 'show' + +const SELECTOR_TITLE = '.popover-header' +const SELECTOR_CONTENT = '.popover-body' + /** * ------------------------------------------------------------------------ * Class Definition @@ -66,10 +68,6 @@ const Event = { class Popover extends Tooltip { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } @@ -78,18 +76,10 @@ class Popover extends Tooltip { return NAME } - static get DATA_KEY() { - return DATA_KEY - } - static get Event() { return Event } - static get EVENT_KEY() { - return EVENT_KEY - } - static get DefaultType() { return DefaultType } @@ -100,50 +90,45 @@ class Popover extends Tooltip { return this.getTitle() || this._getContent() } - addAttachmentClass(attachment) { - $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`) - } - - getTipElement() { - this.tip = this.tip || $(this.config.template)[0] - return this.tip - } - setContent() { - const $tip = $(this.getTipElement()) + const tip = this.getTipElement() - // We use append for html objects to maintain js events - this.setElementContent($tip.find(SELECTOR_TITLE), this.getTitle()) + // we use append for html objects to maintain js events + this.setElementContent(SelectorEngine.findOne(SELECTOR_TITLE, tip), this.getTitle()) let content = this._getContent() if (typeof content === 'function') { - content = content.call(this.element) + content = content.call(this._element) } - this.setElementContent($tip.find(SELECTOR_CONTENT), content) + this.setElementContent(SelectorEngine.findOne(SELECTOR_CONTENT, tip), content) - $tip.removeClass(`${CLASS_NAME_FADE} ${CLASS_NAME_SHOW}`) + tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW) } // Private + _addAttachmentClass(attachment) { + this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`) + } + _getContent() { - return this.element.getAttribute('data-content') || - this.config.content + return this._element.getAttribute('data-bs-content') || this._config.content } _cleanTipClass() { - const $tip = $(this.getTipElement()) - const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX) + const tip = this.getTipElement() + const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX) if (tabClass !== null && tabClass.length > 0) { - $tip.removeClass(tabClass.join('')) + tabClass.map(token => token.trim()) + .forEach(tClass => tip.classList.remove(tClass)) } } // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) + let data = Data.get(this, DATA_KEY) const _config = typeof config === 'object' ? config : null if (!data && /dispose|hide/.test(config)) { @@ -152,7 +137,7 @@ class Popover extends Tooltip { if (!data) { data = new Popover(this, _config) - $(this).data(DATA_KEY, data) + Data.set(this, DATA_KEY, data) } if (typeof config === 'string') { @@ -170,13 +155,9 @@ class Popover extends Tooltip { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Popover to jQuery only if jQuery is present */ -$.fn[NAME] = Popover._jQueryInterface -$.fn[NAME].Constructor = Popover -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Popover._jQueryInterface -} +defineJQueryPlugin(Popover) export default Popover diff --git a/vendor/twbs/bootstrap/js/src/scrollspy.js b/vendor/twbs/bootstrap/js/src/scrollspy.js index 351df0649..3297c45c2 100644 --- a/vendor/twbs/bootstrap/js/src/scrollspy.js +++ b/vendor/twbs/bootstrap/js/src/scrollspy.js @@ -1,12 +1,21 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): scrollspy.js + * Bootstrap (v5.0.1): scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getSelectorFromElement, + getUID, + isElement, + typeCheckConfig +} from './util/index' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,11 +24,9 @@ import Util from './util' */ const NAME = 'scrollspy' -const VERSION = '4.6.0' const DATA_KEY = 'bs.scrollspy' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const Default = { offset: 10, @@ -40,13 +47,12 @@ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item' const CLASS_NAME_ACTIVE = 'active' -const SELECTOR_DATA_SPY = '[data-spy="scroll"]' +const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]' const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group' const SELECTOR_NAV_LINKS = '.nav-link' const SELECTOR_NAV_ITEMS = '.nav-item' const SELECTOR_LIST_ITEMS = '.list-group-item' const SELECTOR_DROPDOWN = '.dropdown' -const SELECTOR_DROPDOWN_ITEMS = '.dropdown-item' const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle' const METHOD_OFFSET = 'offset' @@ -58,20 +64,18 @@ const METHOD_POSITION = 'position' * ------------------------------------------------------------------------ */ -class ScrollSpy { +class ScrollSpy extends BaseComponent { constructor(element, config) { - this._element = element - this._scrollElement = element.tagName === 'BODY' ? window : element + super(element) + this._scrollElement = this._element.tagName === 'BODY' ? window : this._element this._config = this._getConfig(config) - this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS},` + - `${this._config.target} ${SELECTOR_LIST_ITEMS},` + - `${this._config.target} ${SELECTOR_DROPDOWN_ITEMS}` + this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS}, ${this._config.target} ${SELECTOR_LIST_ITEMS}, ${this._config.target} .${CLASS_NAME_DROPDOWN_ITEM}` this._offsets = [] this._targets = [] this._activeTarget = null this._scrollHeight = 0 - $(this._scrollElement).on(EVENT_SCROLL, event => this._process(event)) + EventHandler.on(this._scrollElement, EVENT_SCROLL, () => this._process()) this.refresh() this._process() @@ -79,55 +83,51 @@ class ScrollSpy { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public refresh() { const autoMethod = this._scrollElement === this._scrollElement.window ? - METHOD_OFFSET : METHOD_POSITION + METHOD_OFFSET : + METHOD_POSITION const offsetMethod = this._config.method === 'auto' ? - autoMethod : this._config.method + autoMethod : + this._config.method const offsetBase = offsetMethod === METHOD_POSITION ? - this._getScrollTop() : 0 + this._getScrollTop() : + 0 this._offsets = [] this._targets = [] - this._scrollHeight = this._getScrollHeight() - const targets = [].slice.call(document.querySelectorAll(this._selector)) + const targets = SelectorEngine.find(this._selector) - targets - .map(element => { - let target - const targetSelector = Util.getSelectorFromElement(element) + targets.map(element => { + const targetSelector = getSelectorFromElement(element) + const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null - if (targetSelector) { - target = document.querySelector(targetSelector) - } - - if (target) { - const targetBCR = target.getBoundingClientRect() - if (targetBCR.width || targetBCR.height) { - // TODO (fat): remove sketch reliance on jQuery position/offset - return [ - $(target)[offsetMethod]().top + offsetBase, - targetSelector - ] - } + if (target) { + const targetBCR = target.getBoundingClientRect() + if (targetBCR.width || targetBCR.height) { + return [ + Manipulator[offsetMethod](target).top + offsetBase, + targetSelector + ] } + } - return null - }) + return null + }) .filter(item => item) .sort((a, b) => a[0] - b[0]) .forEach(item => { @@ -137,17 +137,8 @@ class ScrollSpy { } dispose() { - $.removeData(this._element, DATA_KEY) - $(this._scrollElement).off(EVENT_KEY) - - this._element = null - this._scrollElement = null - this._config = null - this._selector = null - this._offsets = null - this._targets = null - this._activeTarget = null - this._scrollHeight = null + EventHandler.off(this._scrollElement, EVENT_KEY) + super.dispose() } // Private @@ -155,27 +146,29 @@ class ScrollSpy { _getConfig(config) { config = { ...Default, + ...Manipulator.getDataAttributes(this._element), ...(typeof config === 'object' && config ? config : {}) } - if (typeof config.target !== 'string' && Util.isElement(config.target)) { - let id = $(config.target).attr('id') + if (typeof config.target !== 'string' && isElement(config.target)) { + let { id } = config.target if (!id) { - id = Util.getUID(NAME) - $(config.target).attr('id', id) + id = getUID(NAME) + config.target.id = id } config.target = `#${id}` } - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } _getScrollTop() { return this._scrollElement === window ? - this._scrollElement.pageYOffset : this._scrollElement.scrollTop + this._scrollElement.pageYOffset : + this._scrollElement.scrollTop } _getScrollHeight() { @@ -187,7 +180,8 @@ class ScrollSpy { _getOffsetHeight() { return this._scrollElement === window ? - window.innerHeight : this._scrollElement.getBoundingClientRect().height + window.innerHeight : + this._scrollElement.getBoundingClientRect().height } _process() { @@ -218,8 +212,7 @@ class ScrollSpy { for (let i = this._offsets.length; i--;) { const isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && - (typeof this._offsets[i + 1] === 'undefined' || - scrollTop < this._offsets[i + 1]) + (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]) if (isActiveTarget) { this._activate(this._targets[i]) @@ -232,62 +225,62 @@ class ScrollSpy { this._clear() - const queries = this._selector - .split(',') - .map(selector => `${selector}[data-target="${target}"],${selector}[href="${target}"]`) + const queries = this._selector.split(',') + .map(selector => `${selector}[data-bs-target="${target}"],${selector}[href="${target}"]`) + + const link = SelectorEngine.findOne(queries.join(',')) - const $link = $([].slice.call(document.querySelectorAll(queries.join(',')))) + if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { + SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN)) + .classList.add(CLASS_NAME_ACTIVE) - if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) { - $link.closest(SELECTOR_DROPDOWN) - .find(SELECTOR_DROPDOWN_TOGGLE) - .addClass(CLASS_NAME_ACTIVE) - $link.addClass(CLASS_NAME_ACTIVE) + link.classList.add(CLASS_NAME_ACTIVE) } else { // Set triggered link as active - $link.addClass(CLASS_NAME_ACTIVE) - // Set triggered links parents as active - // With both