aboutsummaryrefslogtreecommitdiff
path: root/js/src
diff options
context:
space:
mode:
Diffstat (limited to 'js/src')
-rw-r--r--js/src/alert.js18
-rw-r--r--js/src/base-component.js16
-rw-r--r--js/src/button.js20
-rw-r--r--js/src/carousel.js155
-rw-r--r--js/src/collapse.js84
-rw-r--r--js/src/dom/data.js4
-rw-r--r--js/src/dom/event-handler.js14
-rw-r--r--js/src/dom/manipulator.js2
-rw-r--r--js/src/dom/selector-engine.js12
-rw-r--r--js/src/dropdown.js20
-rw-r--r--js/src/modal.js168
-rw-r--r--js/src/offcanvas.js49
-rw-r--r--js/src/popover.js36
-rw-r--r--js/src/scrollspy.js78
-rw-r--r--js/src/tab.js31
-rw-r--r--js/src/toast.js19
-rw-r--r--js/src/tooltip.js290
-rw-r--r--js/src/util/backdrop.js42
-rw-r--r--js/src/util/component-functions.js2
-rw-r--r--js/src/util/focustrap.js30
-rw-r--r--js/src/util/index.js38
-rw-r--r--js/src/util/sanitizer.js4
-rw-r--r--js/src/util/scrollbar.js34
-rw-r--r--js/src/util/swipe.js140
-rw-r--r--js/src/util/template-factory.js161
25 files changed, 728 insertions, 739 deletions
diff --git a/js/src/alert.js b/js/src/alert.js
index 9b4cba4cf..7d4b555ea 100644
--- a/js/src/alert.js
+++ b/js/src/alert.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): alert.js
+ * Bootstrap (v5.1.3): alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -11,9 +11,7 @@ import BaseComponent from './base-component'
import { enableDismissTrigger } from './util/component-functions'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'alert'
@@ -26,20 +24,16 @@ const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Alert extends BaseComponent {
// Getters
-
static get NAME() {
return NAME
}
// Public
-
close() {
const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)
@@ -61,7 +55,6 @@ class Alert extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Alert.getOrCreateInstance(this)
@@ -80,18 +73,13 @@ class Alert extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
enableDismissTrigger(Alert, 'close')
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Alert to jQuery only if jQuery is present
*/
defineJQueryPlugin(Alert)
diff --git a/js/src/base-component.js b/js/src/base-component.js
index 0a1c17357..3c5eb460a 100644
--- a/js/src/base-component.js
+++ b/js/src/base-component.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): base-component.js
+ * Bootstrap (v5.1.3): base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -13,12 +13,14 @@ import {
import EventHandler from './dom/event-handler'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
-const VERSION = '5.1.2'
+const VERSION = '5.1.3'
+
+/**
+ * Class definition
+ */
class BaseComponent {
constructor(element) {
@@ -32,6 +34,7 @@ class BaseComponent {
Data.set(this._element, this.constructor.DATA_KEY, this)
}
+ // Public
dispose() {
Data.remove(this._element, this.constructor.DATA_KEY)
EventHandler.off(this._element, this.constructor.EVENT_KEY)
@@ -45,8 +48,7 @@ class BaseComponent {
executeAfterTransition(callback, element, isAnimated)
}
- /** Static */
-
+ // Static
static getInstance(element) {
return Data.get(getElement(element), this.DATA_KEY)
}
@@ -60,7 +62,7 @@ class BaseComponent {
}
static get NAME() {
- throw new Error('You have to implement the static method "NAME", for each component!')
+ throw new Error('You have to implement the static method "NAME" for each component!')
}
static get DATA_KEY() {
diff --git a/js/src/button.js b/js/src/button.js
index c4e7c296d..e2a52e7eb 100644
--- a/js/src/button.js
+++ b/js/src/button.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): button.js
+ * Bootstrap (v5.1.3): button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -10,9 +10,7 @@ import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'button'
@@ -21,33 +19,26 @@ const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const CLASS_NAME_ACTIVE = 'active'
-
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]'
-
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Button extends BaseComponent {
// Getters
-
static get NAME() {
return NAME
}
// Public
-
toggle() {
// 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) {
return this.each(function () {
const data = Button.getOrCreateInstance(this)
@@ -60,9 +51,7 @@ class Button extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
@@ -75,10 +64,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
})
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Button to jQuery only if jQuery is present
*/
defineJQueryPlugin(Button)
diff --git a/js/src/carousel.js b/js/src/carousel.js
index 3f49ded40..3589f2206 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): carousel.js
+ * Bootstrap (v5.1.3): carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -8,9 +8,9 @@
import {
defineJQueryPlugin,
getElementFromSelector,
+ getNextActiveElement,
isRTL,
isVisible,
- getNextActiveElement,
reflow,
triggerTransitionEnd,
typeCheckConfig
@@ -18,12 +18,11 @@ import {
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
+import Swipe from './util/swipe'
import BaseComponent from './base-component'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'carousel'
@@ -34,46 +33,17 @@ const DATA_API_KEY = '.data-api'
const ARROW_LEFT_KEY = 'ArrowLeft'
const ARROW_RIGHT_KEY = 'ArrowRight'
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
-const SWIPE_THRESHOLD = 40
-
-const Default = {
- interval: 5000,
- keyboard: true,
- slide: false,
- pause: 'hover',
- wrap: true,
- touch: true
-}
-
-const DefaultType = {
- interval: '(number|boolean)',
- keyboard: 'boolean',
- slide: '(boolean|string)',
- pause: '(string|boolean)',
- wrap: 'boolean',
- touch: 'boolean'
-}
const ORDER_NEXT = 'next'
const ORDER_PREV = 'prev'
const DIRECTION_LEFT = 'left'
const DIRECTION_RIGHT = 'right'
-const KEY_TO_DIRECTION = {
- [ARROW_LEFT_KEY]: DIRECTION_RIGHT,
- [ARROW_RIGHT_KEY]: DIRECTION_LEFT
-}
-
const EVENT_SLIDE = `slide${EVENT_KEY}`
const EVENT_SLID = `slid${EVENT_KEY}`
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`
-const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
-const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
-const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
-const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
-const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
const EVENT_DRAG_START = `dragstart${EVENT_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
@@ -85,7 +55,6 @@ 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'
const SELECTOR_ACTIVE = '.active'
const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'
@@ -97,14 +66,33 @@ 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 POINTER_TYPE_TOUCH = 'touch'
-const POINTER_TYPE_PEN = 'pen'
+const KEY_TO_DIRECTION = {
+ [ARROW_LEFT_KEY]: DIRECTION_RIGHT,
+ [ARROW_RIGHT_KEY]: DIRECTION_LEFT
+}
+
+const Default = {
+ interval: 5000,
+ keyboard: true,
+ slide: false,
+ pause: 'hover',
+ wrap: true,
+ touch: true
+}
+
+const DefaultType = {
+ interval: '(number|boolean)',
+ keyboard: 'boolean',
+ slide: '(boolean|string)',
+ pause: '(string|boolean)',
+ wrap: 'boolean',
+ touch: 'boolean'
+}
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
+
class Carousel extends BaseComponent {
constructor(element, config) {
super(element)
@@ -115,19 +103,14 @@ class Carousel extends BaseComponent {
this._isPaused = false
this._isSliding = false
this.touchTimeout = null
- this.touchStartX = 0
- this.touchDeltaX = 0
+ this._swipeHelper = null
this._config = this._getConfig(config)
this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)
- this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
- this._pointerEvent = Boolean(window.PointerEvent)
-
this._addEventListeners()
}
// Getters
-
static get Default() {
return Default
}
@@ -137,7 +120,6 @@ class Carousel extends BaseComponent {
}
// Public
-
next() {
this._slide(ORDER_NEXT)
}
@@ -214,8 +196,15 @@ class Carousel extends BaseComponent {
this._slide(order, this._items[index])
}
- // Private
+ dispose() {
+ if (this._swipeHelper) {
+ this._swipeHelper.dispose()
+ }
+
+ super.dispose()
+ }
+ // Private
_getConfig(config) {
config = {
...Default,
@@ -226,24 +215,6 @@ class Carousel extends BaseComponent {
return config
}
- _handleSwipe() {
- const absDeltax = Math.abs(this.touchDeltaX)
-
- if (absDeltax <= SWIPE_THRESHOLD) {
- return
- }
-
- const direction = absDeltax / this.touchDeltaX
-
- this.touchDeltaX = 0
-
- if (!direction) {
- return
- }
-
- this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT)
- }
-
_addEventListeners() {
if (this._config.keyboard) {
EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))
@@ -254,38 +225,17 @@ class Carousel extends BaseComponent {
EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))
}
- if (this._config.touch && this._touchSupported) {
+ if (this._config.touch && Swipe.isSupported()) {
this._addTouchEventListeners()
}
}
_addTouchEventListeners() {
- const hasPointerPenTouch = event => {
- return this._pointerEvent &&
- (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)
- }
-
- const start = event => {
- if (hasPointerPenTouch(event)) {
- this.touchStartX = event.clientX
- } else if (!this._pointerEvent) {
- this.touchStartX = event.touches[0].clientX
- }
- }
-
- const move = event => {
- // ensure swiping with one touch and not pinching
- this.touchDeltaX = event.touches && event.touches.length > 1 ?
- 0 :
- event.touches[0].clientX - this.touchStartX
+ for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
+ EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())
}
- const end = event => {
- if (hasPointerPenTouch(event)) {
- this.touchDeltaX = event.clientX - this.touchStartX
- }
-
- this._handleSwipe()
+ const endCallBack = () => {
if (this._config.pause === 'hover') {
// If it's a touch-enabled device, mouseenter/leave are fired as
// part of the mouse compatibility events on first tap - the carousel
@@ -304,20 +254,13 @@ class Carousel extends BaseComponent {
}
}
- for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
- EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())
+ const swipeConfig = {
+ leftCallback: () => this._slide(DIRECTION_LEFT),
+ rightCallback: () => this._slide(DIRECTION_RIGHT),
+ endCallback: endCallBack
}
- if (this._pointerEvent) {
- 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 {
- 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))
- }
+ this._swipeHelper = new Swipe(this._element, swipeConfig)
}
_keydown(event) {
@@ -502,7 +445,6 @@ class Carousel extends BaseComponent {
}
// Static
-
static carouselInterface(element, config) {
const data = Carousel.getOrCreateInstance(element, config)
@@ -564,9 +506,7 @@ class Carousel extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)
@@ -580,10 +520,7 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
})
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Carousel to jQuery only if jQuery is present
*/
defineJQueryPlugin(Carousel)
diff --git a/js/src/collapse.js b/js/src/collapse.js
index b7f200d56..642f7e840 100644
--- a/js/src/collapse.js
+++ b/js/src/collapse.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): collapse.js
+ * Bootstrap (v5.1.3): collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -8,21 +8,18 @@
import {
defineJQueryPlugin,
getElement,
- getSelectorFromElement,
getElementFromSelector,
+ getSelectorFromElement,
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'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'collapse'
@@ -30,16 +27,6 @@ const DATA_KEY = 'bs.collapse'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
-const Default = {
- toggle: true,
- parent: null
-}
-
-const DefaultType = {
- toggle: 'boolean',
- parent: '(null|element)'
-}
-
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_HIDE = `hide${EVENT_KEY}`
@@ -59,10 +46,18 @@ const HEIGHT = 'height'
const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]'
+const Default = {
+ toggle: true,
+ parent: null
+}
+
+const DefaultType = {
+ toggle: 'boolean',
+ parent: '(null|element)'
+}
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Collapse extends BaseComponent {
@@ -81,7 +76,6 @@ class Collapse extends BaseComponent {
.filter(foundElem => foundElem === this._element)
if (selector !== null && filterElement.length) {
- this._selector = selector
this._triggerArray.push(elem)
}
}
@@ -98,7 +92,6 @@ class Collapse extends BaseComponent {
}
// Getters
-
static get Default() {
return Default
}
@@ -108,7 +101,6 @@ class Collapse extends BaseComponent {
}
// Public
-
toggle() {
if (this._isShown()) {
this.hide()
@@ -122,23 +114,17 @@ class Collapse extends BaseComponent {
return
}
- let actives = []
- let activesData
+ let activeChildren = []
+ // find active children
if (this._config.parent) {
- const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)
- // remove children if greater depth
- actives = SelectorEngine.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem))
+ activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)
+ .filter(element => element !== this._element)
+ .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))
}
- const container = SelectorEngine.findOne(this._selector)
- if (actives.length) {
- const tempActiveData = actives.find(elem => container !== elem)
- activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null
-
- if (activesData && activesData._isTransitioning) {
- return
- }
+ if (activeChildren.length && activeChildren[0]._isTransitioning) {
+ return
}
const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)
@@ -146,14 +132,8 @@ class Collapse extends BaseComponent {
return
}
- for (const elemActive of actives) {
- if (container !== elemActive) {
- Collapse.getOrCreateInstance(elemActive, { toggle: false }).hide()
- }
-
- if (!activesData) {
- Data.set(elemActive, DATA_KEY, null)
- }
+ for (const activeInstance of activeChildren) {
+ activeInstance.hide()
}
const dimension = this._getDimension()
@@ -230,7 +210,6 @@ class Collapse extends BaseComponent {
}
// Private
-
_getConfig(config) {
config = {
...Default,
@@ -252,10 +231,9 @@ class Collapse extends BaseComponent {
return
}
- const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)
- const elements = SelectorEngine.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem))
+ const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)
- for (const element of elements) {
+ for (const element of children) {
const selected = getElementFromSelector(element)
if (selected) {
@@ -264,6 +242,12 @@ class Collapse extends BaseComponent {
}
}
+ _getFirstLevelChildren(selector) {
+ const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)
+ // remove children if greater depth
+ return SelectorEngine.find(selector, this._config.parent).filter(elem => !children.includes(elem))
+ }
+
_addAriaAndCollapsedClass(triggerArray, isOpen) {
if (!triggerArray.length) {
return
@@ -281,7 +265,6 @@ class Collapse extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const _config = {}
@@ -303,9 +286,7 @@ class Collapse extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
@@ -323,10 +304,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Collapse to jQuery only if jQuery is present
*/
defineJQueryPlugin(Collapse)
diff --git a/js/src/dom/data.js b/js/src/dom/data.js
index 5dbb87754..4209f3188 100644
--- a/js/src/dom/data.js
+++ b/js/src/dom/data.js
@@ -1,14 +1,12 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): dom/data.js
+ * Bootstrap (v5.1.3): dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const elementMap = new Map()
diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js
index e2fdbd52c..b9ebce324 100644
--- a/js/src/dom/event-handler.js
+++ b/js/src/dom/event-handler.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): dom/event-handler.js
+ * Bootstrap (v5.1.3): dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -8,9 +8,7 @@
import { getjQuery } from '../util/index'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const namespaceRegex = /[^.]*(?=\..*)\.|.*/
@@ -73,9 +71,7 @@ const nativeEvents = new Set([
])
/**
- * ------------------------------------------------------------------------
* Private methods
- * ------------------------------------------------------------------------
*/
function getUidEvent(element, uid) {
@@ -143,7 +139,6 @@ function findHandler(events, handler, delegationSelector = null) {
function normalizeParams(originalTypeEvent, handler, delegationFn) {
const delegation = typeof handler === 'string'
const originalHandler = delegation ? delegationFn : handler
-
let typeEvent = getTypeEvent(originalTypeEvent)
const isNative = nativeEvents.has(typeEvent)
@@ -224,7 +219,6 @@ function removeNamespacedHandlers(element, events, typeEvent, namespace) {
for (const handlerKey of Object.keys(storeElementEvent)) {
if (handlerKey.includes(namespace)) {
const event = storeElementEvent[handlerKey]
-
removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
}
}
@@ -277,7 +271,6 @@ const EventHandler = {
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
const event = storeElementEvent[keyHandlers]
-
removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
}
}
@@ -312,10 +305,7 @@ const EventHandler = {
evt = document.createEvent('HTMLEvents')
evt.initEvent(typeEvent, bubbles, true)
} else {
- evt = new CustomEvent(event, {
- bubbles,
- cancelable: true
- })
+ evt = new CustomEvent(event, { bubbles, cancelable: true })
}
// merge custom information in our event
diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js
index 219727d7c..a3e9e192a 100644
--- a/js/src/dom/manipulator.js
+++ b/js/src/dom/manipulator.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): dom/manipulator.js
+ * Bootstrap (v5.1.3): dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index 9fc0e4b0e..af27dc379 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -1,18 +1,16 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): dom/selector-engine.js
+ * Bootstrap (v5.1.3): dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import { isDisabled, isVisible } from '../util/index'
+
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
-import { isDisabled, isVisible } from '../util/index'
-
const NODE_TEXT = 3
const SelectorEngine = {
@@ -25,13 +23,11 @@ const SelectorEngine = {
},
children(element, selector) {
- return [].concat(...element.children)
- .filter(child => child.matches(selector))
+ return [].concat(...element.children).filter(child => child.matches(selector))
},
parents(element, selector) {
const parents = []
-
let ancestor = element.parentNode
while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 32b587bf1..6129707e2 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -1,12 +1,11 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): dropdown.js
+ * Bootstrap (v5.1.3): dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
-
import {
defineJQueryPlugin,
getElement,
@@ -25,9 +24,7 @@ import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'dropdown'
@@ -89,9 +86,7 @@ const DefaultType = {
}
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Dropdown extends BaseComponent {
@@ -105,7 +100,6 @@ class Dropdown extends BaseComponent {
}
// Getters
-
static get Default() {
return Default
}
@@ -119,7 +113,6 @@ class Dropdown extends BaseComponent {
}
// Public
-
toggle() {
return this._isShown() ? this.hide() : this.show()
}
@@ -193,7 +186,6 @@ class Dropdown extends BaseComponent {
}
// Private
-
_completeHide(relatedTarget) {
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)
if (hideEvent.defaultPrevented) {
@@ -354,7 +346,6 @@ class Dropdown extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Dropdown.getOrCreateInstance(this, config)
@@ -474,9 +465,7 @@ class Dropdown extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
@@ -489,10 +478,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Dropdown to jQuery only if jQuery is present
*/
defineJQueryPlugin(Dropdown)
diff --git a/js/src/modal.js b/js/src/modal.js
index b0ee089de..b8b144774 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): modal.js
+ * Bootstrap (v5.1.3): modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -23,9 +23,7 @@ import FocusTrap from './util/focustrap'
import { enableDismissTrigger } from './util/component-functions'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'modal'
@@ -34,18 +32,6 @@ const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const ESCAPE_KEY = 'Escape'
-const Default = {
- backdrop: true,
- keyboard: true,
- focus: true
-}
-
-const DefaultType = {
- backdrop: '(boolean|string)',
- keyboard: 'boolean',
- focus: 'boolean'
-}
-
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
@@ -54,8 +40,6 @@ const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_RESIZE = `resize${EVENT_KEY}`
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
-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_OPEN = 'modal-open'
@@ -68,10 +52,20 @@ const SELECTOR_DIALOG = '.modal-dialog'
const SELECTOR_MODAL_BODY = '.modal-body'
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]'
+const Default = {
+ backdrop: true,
+ keyboard: true,
+ focus: true
+}
+
+const DefaultType = {
+ backdrop: '(boolean|string)',
+ keyboard: 'boolean',
+ focus: 'boolean'
+}
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Modal extends BaseComponent {
@@ -83,13 +77,11 @@ class Modal extends BaseComponent {
this._backdrop = this._initializeBackDrop()
this._focustrap = this._initializeFocusTrap()
this._isShown = false
- this._ignoreBackdropClick = false
this._isTransitioning = false
this._scrollBar = new ScrollBarHelper()
}
// Getters
-
static get Default() {
return Default
}
@@ -99,7 +91,6 @@ class Modal extends BaseComponent {
}
// Public
-
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget)
}
@@ -118,10 +109,7 @@ class Modal extends BaseComponent {
}
this._isShown = true
-
- if (this._isAnimated()) {
- this._isTransitioning = true
- }
+ this._isTransitioning = true
this._scrollBar.hide()
@@ -129,16 +117,8 @@ class Modal extends BaseComponent {
this._adjustDialog()
- this._setEscapeEvent()
- this._setResizeEvent()
-
- EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
- EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {
- if (event.target === this._element) {
- this._ignoreBackdropClick = true
- }
- })
- })
+ this._toggleEscapeEventListener(true)
+ this._toggleResizeEventListener(true)
this._showBackdrop(() => this._showElement(relatedTarget))
}
@@ -155,23 +135,16 @@ class Modal extends BaseComponent {
}
this._isShown = false
- const isAnimated = this._isAnimated()
+ this._isTransitioning = true
- if (isAnimated) {
- this._isTransitioning = true
- }
-
- this._setEscapeEvent()
- this._setResizeEvent()
+ this._toggleEscapeEventListener(false)
+ this._toggleResizeEventListener(false)
this._focustrap.deactivate()
this._element.classList.remove(CLASS_NAME_SHOW)
- EventHandler.off(this._element, EVENT_CLICK_DISMISS)
- EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS)
-
- this._queueCallback(() => this._hideModal(), this._element, isAnimated)
+ this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())
}
dispose() {
@@ -189,7 +162,6 @@ class Modal extends BaseComponent {
}
// Private
-
_initializeBackDrop() {
return new Backdrop({
isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value
@@ -214,11 +186,8 @@ class Modal extends BaseComponent {
}
_showElement(relatedTarget) {
- const isAnimated = this._isAnimated()
- const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)
-
- if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
- // Don't move modal's DOM position
+ // try to append dynamic modal
+ if (!document.body.contains(this._element)) {
document.body.append(this._element)
}
@@ -228,13 +197,12 @@ class Modal extends BaseComponent {
this._element.setAttribute('role', 'dialog')
this._element.scrollTop = 0
+ const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)
if (modalBody) {
modalBody.scrollTop = 0
}
- if (isAnimated) {
- reflow(this._element)
- }
+ reflow(this._element)
this._element.classList.add(CLASS_NAME_SHOW)
@@ -249,30 +217,37 @@ class Modal extends BaseComponent {
})
}
- this._queueCallback(transitionComplete, this._dialog, isAnimated)
+ this._queueCallback(transitionComplete, this._dialog, this._isAnimated())
}
- _setEscapeEvent() {
- if (this._isShown) {
- 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.key === ESCAPE_KEY) {
- this._triggerBackdropTransition()
- }
- })
- } else {
+ _toggleEscapeEventListener(enable) {
+ if (!enable) {
EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)
+ return
}
+
+ EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
+ if (event.key !== ESCAPE_KEY) {
+ return
+ }
+
+ if (this._config.keyboard) {
+ event.preventDefault()
+ this.hide()
+ return
+ }
+
+ this._triggerBackdropTransition()
+ })
}
- _setResizeEvent() {
- if (this._isShown) {
+ _toggleResizeEventListener(enable) {
+ if (enable) {
EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())
- } else {
- EventHandler.off(window, EVENT_RESIZE)
+ return
}
+
+ EventHandler.off(window, EVENT_RESIZE)
}
_hideModal() {
@@ -281,6 +256,7 @@ class Modal extends BaseComponent {
this._element.removeAttribute('aria-modal')
this._element.removeAttribute('role')
this._isTransitioning = false
+
this._backdrop.hide(() => {
document.body.classList.remove(CLASS_NAME_OPEN)
this._resetAdjustments()
@@ -291,18 +267,16 @@ class Modal extends BaseComponent {
_showBackdrop(callback) {
EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
- if (this._ignoreBackdropClick) {
- this._ignoreBackdropClick = false
- return
- }
-
if (event.target !== event.currentTarget) {
return
}
if (this._config.backdrop === true) {
this.hide()
- } else if (this._config.backdrop === 'static') {
+ return
+ }
+
+ if (this._config.backdrop === 'static') {
this._triggerBackdropTransition()
}
})
@@ -322,9 +296,9 @@ class Modal extends BaseComponent {
const { classList, scrollHeight, style } = this._element
const isModalOverflowing = scrollHeight > document.documentElement.clientHeight
-
+ const initialOverflowY = style.overflowY
// return if the following background transition hasn't yet completed
- if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) {
+ if (initialOverflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
return
}
@@ -335,31 +309,31 @@ class Modal extends BaseComponent {
classList.add(CLASS_NAME_STATIC)
this._queueCallback(() => {
classList.remove(CLASS_NAME_STATIC)
- if (!isModalOverflowing) {
- this._queueCallback(() => {
- style.overflowY = ''
- }, this._dialog)
- }
+ this._queueCallback(() => {
+ style.overflowY = initialOverflowY
+ }, this._dialog)
}, this._dialog)
this._element.focus()
}
- // ----------------------------------------------------------------------
- // the following methods are used to handle overflowing modals
- // ----------------------------------------------------------------------
+ /**
+ * The following methods are used to handle overflowing modals
+ */
_adjustDialog() {
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
const scrollbarWidth = this._scrollBar.getWidth()
const isBodyOverflowing = scrollbarWidth > 0
- if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) {
- this._element.style.paddingLeft = `${scrollbarWidth}px`
+ if (isBodyOverflowing && !isModalOverflowing) {
+ const property = isRTL() ? 'paddingLeft' : 'paddingRight'
+ this._element.style[property] = `${scrollbarWidth}px`
}
- if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) {
- this._element.style.paddingRight = `${scrollbarWidth}px`
+ if (!isBodyOverflowing && isModalOverflowing) {
+ const property = isRTL() ? 'paddingRight' : 'paddingLeft'
+ this._element.style[property] = `${scrollbarWidth}px`
}
}
@@ -369,7 +343,6 @@ class Modal extends BaseComponent {
}
// Static
-
static jQueryInterface(config, relatedTarget) {
return this.each(function () {
const data = Modal.getOrCreateInstance(this, config)
@@ -388,9 +361,7 @@ class Modal extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
@@ -427,10 +398,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
enableDismissTrigger(Modal)
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Modal to jQuery only if jQuery is present
*/
defineJQueryPlugin(Modal)
diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js
index 28fd49f06..6878b1f62 100644
--- a/js/src/offcanvas.js
+++ b/js/src/offcanvas.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): offcanvas.js
+ * Bootstrap (v5.1.3): offcanvas.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -22,9 +22,7 @@ import FocusTrap from './util/focustrap'
import { enableDismissTrigger } from './util/component-functions'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'offcanvas'
@@ -34,18 +32,6 @@ const DATA_API_KEY = '.data-api'
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const ESCAPE_KEY = 'Escape'
-const Default = {
- backdrop: true,
- keyboard: true,
- scroll: false
-}
-
-const DefaultType = {
- backdrop: 'boolean',
- keyboard: 'boolean',
- scroll: 'boolean'
-}
-
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'
const OPEN_SELECTOR = '.offcanvas.show'
@@ -59,10 +45,20 @@ const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="offcanvas"]'
+const Default = {
+ backdrop: true,
+ keyboard: true,
+ scroll: false
+}
+
+const DefaultType = {
+ backdrop: 'boolean',
+ keyboard: 'boolean',
+ scroll: 'boolean'
+}
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Offcanvas extends BaseComponent {
@@ -77,7 +73,6 @@ class Offcanvas extends BaseComponent {
}
// Getters
-
static get NAME() {
return NAME
}
@@ -87,7 +82,6 @@ class Offcanvas extends BaseComponent {
}
// Public
-
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget)
}
@@ -168,7 +162,6 @@ class Offcanvas extends BaseComponent {
}
// Private
-
_getConfig(config) {
config = {
...Default,
@@ -204,7 +197,6 @@ class Offcanvas extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Offcanvas.getOrCreateInstance(this, config)
@@ -223,9 +215,7 @@ class Offcanvas extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
@@ -247,9 +237,9 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
// avoid conflict when clicking a toggler of an offcanvas, while another is open
- const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)
- if (allReadyOpen && allReadyOpen !== target) {
- Offcanvas.getInstance(allReadyOpen).hide()
+ const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)
+ if (alreadyOpen && alreadyOpen !== target) {
+ Offcanvas.getInstance(alreadyOpen).hide()
}
const data = Offcanvas.getOrCreateInstance(target)
@@ -263,10 +253,9 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
})
enableDismissTrigger(Offcanvas)
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
*/
defineJQueryPlugin(Offcanvas)
diff --git a/js/src/popover.js b/js/src/popover.js
index d499bedf1..19c1e42a4 100644
--- a/js/src/popover.js
+++ b/js/src/popover.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): popover.js
+ * Bootstrap (v5.1.3): popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -9,15 +9,15 @@ import { defineJQueryPlugin } from './util/index'
import Tooltip from './tooltip'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'popover'
const DATA_KEY = 'bs.popover'
const EVENT_KEY = `.${DATA_KEY}`
-const CLASS_PREFIX = 'bs-popover'
+
+const SELECTOR_TITLE = '.popover-header'
+const SELECTOR_CONTENT = '.popover-body'
const Default = {
...Tooltip.Default,
@@ -50,18 +50,12 @@ const Event = {
MOUSELEAVE: `mouseleave${EVENT_KEY}`
}
-const SELECTOR_TITLE = '.popover-header'
-const SELECTOR_CONTENT = '.popover-body'
-
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Popover extends Tooltip {
// Getters
-
static get Default() {
return Default
}
@@ -79,28 +73,23 @@ class Popover extends Tooltip {
}
// Overrides
-
isWithContent() {
return this.getTitle() || this._getContent()
}
- setContent(tip) {
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE)
- this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT)
- }
-
// Private
+ _getContentForTemplate() {
+ return {
+ [SELECTOR_TITLE]: this.getTitle(),
+ [SELECTOR_CONTENT]: this._getContent()
+ }
+ }
_getContent() {
return this._resolvePossibleFunction(this._config.content)
}
- _getBasicClassPrefix() {
- return CLASS_PREFIX
- }
-
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Popover.getOrCreateInstance(this, config)
@@ -117,10 +106,7 @@ class Popover extends Tooltip {
}
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Popover to jQuery only if jQuery is present
*/
defineJQueryPlugin(Popover)
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index df9a14e22..27bc0cd87 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): scrollspy.js
+ * Bootstrap (v5.1.3): scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -17,9 +17,7 @@ import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'scrollspy'
@@ -27,18 +25,6 @@ const DATA_KEY = 'bs.scrollspy'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
-const Default = {
- offset: 10,
- method: 'auto',
- target: ''
-}
-
-const DefaultType = {
- offset: 'number',
- method: 'string',
- target: '(string|element)'
-}
-
const EVENT_ACTIVATE = `activate${EVENT_KEY}`
const EVENT_SCROLL = `scroll${EVENT_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
@@ -58,10 +44,20 @@ const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
const METHOD_OFFSET = 'offset'
const METHOD_POSITION = 'position'
+const Default = {
+ offset: 10,
+ method: 'auto',
+ target: ''
+}
+
+const DefaultType = {
+ offset: 'number',
+ method: 'string',
+ target: '(string|element)'
+}
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class ScrollSpy extends BaseComponent {
@@ -81,7 +77,6 @@ class ScrollSpy extends BaseComponent {
}
// Getters
-
static get Default() {
return Default
}
@@ -91,7 +86,6 @@ class ScrollSpy extends BaseComponent {
}
// Public
-
refresh() {
const autoMethod = this._scrollElement === this._scrollElement.window ?
METHOD_OFFSET :
@@ -110,25 +104,26 @@ class ScrollSpy extends BaseComponent {
this._scrollHeight = this._getScrollHeight()
const targets = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)
-
- for (const item of targets.map(element => {
- const targetSelector = getSelectorFromElement(element)
- const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null
-
- if (target) {
- const targetBCR = target.getBoundingClientRect()
- if (targetBCR.width || targetBCR.height) {
- return [
- Manipulator[offsetMethod](target).top + offsetBase,
- targetSelector
- ]
+ .map(element => {
+ const targetSelector = getSelectorFromElement(element)
+ const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null
+
+ if (target) {
+ const targetBCR = target.getBoundingClientRect()
+ if (targetBCR.width || targetBCR.height) {
+ return [
+ Manipulator[offsetMethod](target).top + offsetBase,
+ targetSelector
+ ]
+ }
}
- }
- return null
- })
- .filter(item => item)
- .sort((a, b) => a[0] - b[0])) {
+ return null
+ })
+ .filter(item => item)
+ .sort((a, b) => a[0] - b[0])
+
+ for (const item of targets) {
this._offsets.push(item[0])
this._targets.push(item[1])
}
@@ -140,7 +135,6 @@ class ScrollSpy extends BaseComponent {
}
// Private
-
_getConfig(config) {
config = {
...Default,
@@ -256,7 +250,6 @@ class ScrollSpy extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = ScrollSpy.getOrCreateInstance(this, config)
@@ -275,9 +268,7 @@ class ScrollSpy extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
@@ -287,10 +278,7 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
})
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .ScrollSpy to jQuery only if jQuery is present
*/
defineJQueryPlugin(ScrollSpy)
diff --git a/js/src/tab.js b/js/src/tab.js
index dd62df505..4a018ca77 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): tab.js
+ * Bootstrap (v5.1.3): tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -16,9 +16,7 @@ import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'tab'
@@ -46,20 +44,16 @@ const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Tab extends BaseComponent {
// Getters
-
static get NAME() {
return NAME
}
// Public
-
show() {
if ((this._element.parentNode &&
this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
@@ -78,9 +72,7 @@ class Tab extends BaseComponent {
}
const hideEvent = previous ?
- EventHandler.trigger(previous, EVENT_HIDE, {
- relatedTarget: this._element
- }) :
+ EventHandler.trigger(previous, EVENT_HIDE, { relatedTarget: this._element }) :
null
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
@@ -94,12 +86,8 @@ class Tab extends BaseComponent {
this._activate(this._element, listElement)
const complete = () => {
- EventHandler.trigger(previous, EVENT_HIDDEN, {
- relatedTarget: this._element
- })
- EventHandler.trigger(this._element, EVENT_SHOWN, {
- relatedTarget: previous
- })
+ EventHandler.trigger(previous, EVENT_HIDDEN, { relatedTarget: this._element })
+ EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget: previous })
}
if (target) {
@@ -110,7 +98,6 @@ class Tab extends BaseComponent {
}
// Private
-
_activate(element, container, callback) {
const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ?
SelectorEngine.find(SELECTOR_ACTIVE_UL, container) :
@@ -178,7 +165,6 @@ class Tab extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Tab.getOrCreateInstance(this)
@@ -195,9 +181,7 @@ class Tab extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
@@ -214,10 +198,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Tab to jQuery only if jQuery is present
*/
defineJQueryPlugin(Tab)
diff --git a/js/src/toast.js b/js/src/toast.js
index 0faecb8e4..c45721c8f 100644
--- a/js/src/toast.js
+++ b/js/src/toast.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): toast.js
+ * Bootstrap (v5.1.3): toast.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -16,9 +16,7 @@ import BaseComponent from './base-component'
import { enableDismissTrigger } from './util/component-functions'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'toast'
@@ -52,9 +50,7 @@ const Default = {
}
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Toast extends BaseComponent {
@@ -69,7 +65,6 @@ class Toast extends BaseComponent {
}
// Getters
-
static get DefaultType() {
return DefaultType
}
@@ -83,7 +78,6 @@ class Toast extends BaseComponent {
}
// Public
-
show() {
const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)
@@ -145,7 +139,6 @@ class Toast extends BaseComponent {
}
// Private
-
_getConfig(config) {
config = {
...Default,
@@ -212,7 +205,6 @@ class Toast extends BaseComponent {
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Toast.getOrCreateInstance(this, config)
@@ -228,13 +220,14 @@ class Toast extends BaseComponent {
}
}
+/**
+ * Data API implementation
+ */
+
enableDismissTrigger(Toast)
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Toast to jQuery only if jQuery is present
*/
defineJQueryPlugin(Toast)
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index afd17da53..29be4d8d2 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -1,60 +1,53 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): tooltip.js
+ * Bootstrap (v5.1.3): tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
-
import {
defineJQueryPlugin,
findShadowRoot,
getElement,
getUID,
- isElement,
isRTL,
noop,
typeCheckConfig
} from './util/index'
-import { DefaultAllowlist, sanitizeHtml } from './util/sanitizer'
+import { DefaultAllowlist } from './util/sanitizer'
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'
+import TemplateFactory from './util/template-factory'
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'tooltip'
const DATA_KEY = 'bs.tooltip'
const EVENT_KEY = `.${DATA_KEY}`
-const CLASS_PREFIX = 'bs-tooltip'
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])
-const DefaultType = {
- animation: 'boolean',
- template: 'string',
- title: '(string|element|function)',
- trigger: 'string',
- delay: '(number|object)',
- html: 'boolean',
- selector: '(string|boolean)',
- placement: '(string|function)',
- offset: '(array|string|function)',
- container: '(string|element|boolean)',
- fallbackPlacements: 'array',
- boundary: '(string|element)',
- customClass: '(string|function)',
- sanitize: 'boolean',
- sanitizeFn: '(null|function)',
- allowList: 'object',
- popperConfig: '(null|object|function)'
-}
+const CLASS_NAME_FADE = 'fade'
+const CLASS_NAME_MODAL = 'modal'
+const CLASS_NAME_SHOW = 'show'
+
+const HOVER_STATE_SHOW = 'show'
+const HOVER_STATE_OUT = 'out'
+
+const SELECTOR_TOOLTIP_ARROW = '.tooltip-arrow'
+const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
+const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
+
+const EVENT_MODAL_HIDE = 'hide.bs.modal'
+
+const TRIGGER_HOVER = 'hover'
+const TRIGGER_FOCUS = 'focus'
+const TRIGGER_CLICK = 'click'
+const TRIGGER_MANUAL = 'manual'
const AttachmentMap = {
AUTO: 'auto',
@@ -87,6 +80,26 @@ const Default = {
popperConfig: null
}
+const DefaultType = {
+ animation: 'boolean',
+ template: 'string',
+ title: '(string|element|function)',
+ trigger: 'string',
+ delay: '(number|object)',
+ html: 'boolean',
+ selector: '(string|boolean)',
+ placement: '(string|function)',
+ offset: '(array|string|function)',
+ container: '(string|element|boolean)',
+ fallbackPlacements: 'array',
+ boundary: '(string|element)',
+ customClass: '(string|function)',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ allowList: 'object',
+ popperConfig: '(null|object|function)'
+}
+
const Event = {
HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`,
@@ -100,27 +113,8 @@ const Event = {
MOUSELEAVE: `mouseleave${EVENT_KEY}`
}
-const CLASS_NAME_FADE = 'fade'
-const CLASS_NAME_MODAL = 'modal'
-const CLASS_NAME_SHOW = 'show'
-
-const HOVER_STATE_SHOW = 'show'
-const HOVER_STATE_OUT = 'out'
-
-const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
-const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
-
-const EVENT_MODAL_HIDE = 'hide.bs.modal'
-
-const TRIGGER_HOVER = 'hover'
-const TRIGGER_FOCUS = 'focus'
-const TRIGGER_CLICK = 'click'
-const TRIGGER_MANUAL = 'manual'
-
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
class Tooltip extends BaseComponent {
@@ -131,12 +125,13 @@ class Tooltip extends BaseComponent {
super(element)
- // private
+ // Private
this._isEnabled = true
this._timeout = 0
this._hoverState = ''
this._activeTrigger = {}
this._popper = null
+ this._templateFactory = null
// Protected
this._config = this._getConfig(config)
@@ -146,7 +141,6 @@ class Tooltip extends BaseComponent {
}
// Getters
-
static get Default() {
return Default
}
@@ -164,7 +158,6 @@ class Tooltip extends BaseComponent {
}
// Public
-
enable() {
this._isEnabled = true
}
@@ -234,30 +227,9 @@ class Tooltip extends BaseComponent {
return
}
- // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`
- // This will be removed later in favor of a `setContent` method
- if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
- this._disposePopper()
- this.tip.remove()
- this.tip = null
- }
-
const tip = this.getTipElement()
- const tipId = getUID(this.constructor.NAME)
- tip.setAttribute('id', tipId)
- this._element.setAttribute('aria-describedby', tipId)
-
- if (this._config.animation) {
- tip.classList.add(CLASS_NAME_FADE)
- }
-
- const placement = typeof this._config.placement === 'function' ?
- this._config.placement.call(this, tip, this._element) :
- this._config.placement
-
- const attachment = this._getAttachment(placement)
- this._addAttachmentClass(attachment)
+ this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
const { container } = this._config
Data.set(tip, this.constructor.DATA_KEY, this)
@@ -270,16 +242,15 @@ class Tooltip extends BaseComponent {
if (this._popper) {
this._popper.update()
} else {
+ const placement = typeof this._config.placement === 'function' ?
+ this._config.placement.call(this, tip, this._element) :
+ this._config.placement
+ const attachment = AttachmentMap[placement.toUpperCase()]
this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
}
tip.classList.add(CLASS_NAME_SHOW)
- const customClass = this._resolvePossibleFunction(this._config.customClass)
- if (customClass) {
- tip.classList.add(...customClass.split(' '))
- }
-
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
@@ -320,7 +291,6 @@ class Tooltip extends BaseComponent {
tip.remove()
}
- this._cleanTipClass()
this._element.removeAttribute('aria-describedby')
EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
@@ -358,7 +328,6 @@ class Tooltip extends BaseComponent {
}
// Protected
-
isWithContent() {
return Boolean(this.getTitle())
}
@@ -368,69 +337,65 @@ class Tooltip extends BaseComponent {
return this.tip
}
- const element = document.createElement('div')
- element.innerHTML = this._config.template
+ const templateFactory = this._getTemplateFactory(this._getContentForTemplate())
- const tip = element.children[0]
- this.setContent(tip)
+ const tip = templateFactory.toHtml()
tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
+ // todo on v6 the following can be done on css only
+ tip.classList.add(`bs-${this.constructor.NAME}-auto`)
- this.tip = tip
- return this.tip
- }
+ const tipId = getUID(this.constructor.NAME).toString()
- setContent(tip) {
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER)
- }
-
- _sanitizeAndSetContent(template, content, selector) {
- const templateElement = SelectorEngine.findOne(selector, template)
+ tip.setAttribute('id', tipId)
- if (!content && templateElement) {
- templateElement.remove()
- return
+ if (this._config.animation) {
+ tip.classList.add(CLASS_NAME_FADE)
}
- // we use append for html objects to maintain js events
- this.setElementContent(templateElement, content)
+ this.tip = tip
+ return this.tip
}
- setElementContent(element, content) {
- if (element === null) {
- return
+ setContent(content) {
+ let isShown = false
+ if (this.tip) {
+ isShown = this.tip.classList.contains(CLASS_NAME_SHOW)
+ this.tip.remove()
}
- if (isElement(content)) {
- content = getElement(content)
+ this._disposePopper()
- // content is a DOM node or a jQuery
- if (this._config.html) {
- if (content.parentNode !== element) {
- element.innerHTML = ''
- element.append(content)
- }
- } else {
- element.textContent = content.textContent
- }
+ this.tip = this._getTemplateFactory(content).toHtml()
- return
+ if (isShown) {
+ this.show()
}
+ }
- if (this._config.html) {
- if (this._config.sanitize) {
- content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn)
- }
-
- element.innerHTML = content
+ _getTemplateFactory(content) {
+ if (this._templateFactory) {
+ this._templateFactory.changeContent(content)
} else {
- element.textContent = content
+ this._templateFactory = new TemplateFactory({
+ ...this._config,
+ // the `content` var has to be after `this._config`
+ // to override config.content in case of popover
+ content,
+ extraClass: this._resolvePossibleFunction(this._config.customClass)
+ })
}
+
+ return this._templateFactory
}
- getTitle() {
- const title = this._element.getAttribute('data-bs-original-title') || this._config.title
+ _getContentForTemplate() {
+ return {
+ [SELECTOR_TOOLTIP_INNER]: this.getTitle()
+ }
+ }
- return this._resolvePossibleFunction(title)
+ getTitle() {
+ return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('title')
}
updateAttachment(attachment) {
@@ -446,7 +411,6 @@ class Tooltip extends BaseComponent {
}
// Private
-
_initializeOnDelegatedTarget(event, context) {
return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
}
@@ -465,8 +429,8 @@ class Tooltip extends BaseComponent {
return offset
}
- _resolvePossibleFunction(content) {
- return typeof content === 'function' ? content.call(this._element) : content
+ _resolvePossibleFunction(arg) {
+ return typeof arg === 'function' ? arg.call(this._element) : arg
}
_getPopperConfig(attachment) {
@@ -494,21 +458,10 @@ class Tooltip extends BaseComponent {
{
name: 'arrow',
options: {
- element: `.${this.constructor.NAME}-arrow`
+ element: SELECTOR_TOOLTIP_ARROW
}
- },
- {
- name: 'onChange',
- enabled: true,
- phase: 'afterWrite',
- fn: data => this._handlePopperPlacementChange(data)
}
- ],
- onFirstUpdate: data => {
- if (data.options.placement !== data.placement) {
- this._handlePopperPlacementChange(data)
- }
- }
+ ]
}
return {
@@ -517,14 +470,6 @@ class Tooltip extends BaseComponent {
}
}
- _addAttachmentClass(attachment) {
- this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`)
- }
-
- _getAttachment(placement) {
- return AttachmentMap[placement.toUpperCase()]
- }
-
_setListeners() {
const triggers = this._config.trigger.split(' ')
@@ -565,15 +510,9 @@ class Tooltip extends BaseComponent {
_fixTitle() {
const title = this._element.getAttribute('title')
- const originalTitleType = typeof this._element.getAttribute('data-bs-original-title')
-
- if (title || originalTitleType !== 'string') {
- this._element.setAttribute('data-bs-original-title', title || '')
- if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
- this._element.setAttribute('aria-label', title)
- }
- this._element.setAttribute('title', '')
+ if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
+ this._element.setAttribute('aria-label', title)
}
}
@@ -637,13 +576,7 @@ class Tooltip extends BaseComponent {
}
_isWithActiveTrigger() {
- for (const trigger in this._activeTrigger) {
- if (this._activeTrigger[trigger]) {
- return true
- }
- }
-
- return false
+ return Object.values(this._activeTrigger).includes(true)
}
_getConfig(config) {
@@ -679,11 +612,6 @@ class Tooltip extends BaseComponent {
}
typeCheckConfig(NAME, config, this.constructor.DefaultType)
-
- if (config.sanitize) {
- config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)
- }
-
return config
}
@@ -702,33 +630,6 @@ class Tooltip extends BaseComponent {
return config
}
- _cleanTipClass() {
- const tip = this.getTipElement()
- const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g')
- const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex)
- if (tabClass !== null && tabClass.length > 0) {
- for (const tClass of tabClass.map(token => token.trim())) {
- tip.classList.remove(tClass)
- }
- }
- }
-
- _getBasicClassPrefix() {
- return CLASS_PREFIX
- }
-
- _handlePopperPlacementChange(popperData) {
- const { state } = popperData
-
- if (!state) {
- return
- }
-
- this.tip = state.elements.popper
- this._cleanTipClass()
- this._addAttachmentClass(this._getAttachment(state.placement))
- }
-
_disposePopper() {
if (this._popper) {
this._popper.destroy()
@@ -754,10 +655,7 @@ class Tooltip extends BaseComponent {
}
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Tooltip to jQuery only if jQuery is present
*/
defineJQueryPlugin(Tooltip)
diff --git a/js/src/util/backdrop.js b/js/src/util/backdrop.js
index e5ca0c860..fb1b2776b 100644
--- a/js/src/util/backdrop.js
+++ b/js/src/util/backdrop.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): util/backdrop.js
+ * Bootstrap (v5.1.3): util/backdrop.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -8,6 +8,15 @@
import EventHandler from '../dom/event-handler'
import { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index'
+/**
+ * Constants
+ */
+
+const NAME = 'backdrop'
+const CLASS_NAME_FADE = 'fade'
+const CLASS_NAME_SHOW = 'show'
+const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`
+
const Default = {
className: 'modal-backdrop',
isVisible: true, // if false, we use the backdrop helper without adding any element to the dom
@@ -23,11 +32,10 @@ const DefaultType = {
rootElement: '(element|string)',
clickCallback: '(function|null)'
}
-const NAME = 'backdrop'
-const CLASS_NAME_FADE = 'fade'
-const CLASS_NAME_SHOW = 'show'
-const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`
+/**
+ * Class definition
+ */
class Backdrop {
constructor(config) {
@@ -36,6 +44,7 @@ class Backdrop {
this._element = null
}
+ // Public
show(callback) {
if (!this._config.isVisible) {
execute(callback)
@@ -69,8 +78,18 @@ class Backdrop {
})
}
- // Private
+ dispose() {
+ if (!this._isAppended) {
+ return
+ }
+ EventHandler.off(this._element, EVENT_MOUSEDOWN)
+
+ this._element.remove()
+ this._isAppended = false
+ }
+
+ // Private
_getElement() {
if (!this._element) {
const backdrop = document.createElement('div')
@@ -111,17 +130,6 @@ class Backdrop {
this._isAppended = true
}
- dispose() {
- if (!this._isAppended) {
- return
- }
-
- EventHandler.off(this._element, EVENT_MOUSEDOWN)
-
- this._element.remove()
- this._isAppended = false
- }
-
_emulateAnimation(callback) {
executeAfterTransition(callback, this._getElement(), this._config.isAnimated)
}
diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js
index c678ecadf..bd44c3fdc 100644
--- a/js/src/util/component-functions.js
+++ b/js/src/util/component-functions.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): util/component-functions.js
+ * Bootstrap (v5.1.3): util/component-functions.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js
index 500a5b3bc..a1975f489 100644
--- a/js/src/util/focustrap.js
+++ b/js/src/util/focustrap.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): util/focustrap.js
+ * Bootstrap (v5.1.3): util/focustrap.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -9,15 +9,9 @@ import EventHandler from '../dom/event-handler'
import SelectorEngine from '../dom/selector-engine'
import { typeCheckConfig } from './index'
-const Default = {
- trapElement: null, // The element to trap focus inside of
- autofocus: true
-}
-
-const DefaultType = {
- trapElement: 'element',
- autofocus: 'boolean'
-}
+/**
+ * Constants
+ */
const NAME = 'focustrap'
const DATA_KEY = 'bs.focustrap'
@@ -29,6 +23,20 @@ const TAB_KEY = 'Tab'
const TAB_NAV_FORWARD = 'forward'
const TAB_NAV_BACKWARD = 'backward'
+const Default = {
+ trapElement: null, // The element to trap focus inside of
+ autofocus: true
+}
+
+const DefaultType = {
+ trapElement: 'element',
+ autofocus: 'boolean'
+}
+
+/**
+ * Class definition
+ */
+
class FocusTrap {
constructor(config) {
this._config = this._getConfig(config)
@@ -36,6 +44,7 @@ class FocusTrap {
this._lastTabNavDirection = null
}
+ // Public
activate() {
const { trapElement, autofocus } = this._config
@@ -64,7 +73,6 @@ class FocusTrap {
}
// Private
-
_handleFocusin(event) {
const { target } = event
const { trapElement } = this._config
diff --git a/js/src/util/index.js b/js/src/util/index.js
index 0ac4ed263..0ba6ce6f8 100644
--- a/js/src/util/index.js
+++ b/js/src/util/index.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): util/index.js
+ * Bootstrap (v5.1.3): util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -19,9 +19,7 @@ const toType = obj => {
}
/**
- * --------------------------------------------------------------------------
- * Public Util Api
- * --------------------------------------------------------------------------
+ * Public Util API
*/
const getUID = prefix => {
@@ -113,7 +111,8 @@ const isElement = obj => {
}
const getElement = obj => {
- if (isElement(obj)) { // it's a jQuery object or a node element
+ // it's a jQuery object or a node element
+ if (isElement(obj)) {
return obj.jquery ? obj[0] : obj
}
@@ -196,8 +195,7 @@ const noop = () => {}
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
*/
const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight
+ element.offsetHeight // eslint-disable-line no-unused-expressions
}
const getjQuery = () => {
@@ -312,24 +310,24 @@ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed
}
export {
+ defineJQueryPlugin,
+ execute,
+ executeAfterTransition,
+ findShadowRoot,
getElement,
- getUID,
- getSelectorFromElement,
getElementFromSelector,
+ getjQuery,
+ getNextActiveElement,
+ getSelectorFromElement,
getTransitionDurationFromElement,
- triggerTransitionEnd,
+ getUID,
+ isDisabled,
isElement,
- typeCheckConfig,
+ isRTL,
isVisible,
- isDisabled,
- findShadowRoot,
noop,
- getNextActiveElement,
- reflow,
- getjQuery,
onDOMContentLoaded,
- isRTL,
- defineJQueryPlugin,
- execute,
- executeAfterTransition
+ reflow,
+ triggerTransitionEnd,
+ typeCheckConfig
}
diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js
index f5a8287cd..5a7a68035 100644
--- a/js/src/util/sanitizer.js
+++ b/js/src/util/sanitizer.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): util/sanitizer.js
+ * Bootstrap (v5.1.3): util/sanitizer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -45,7 +45,7 @@ const allowedAttribute = (attribute, allowedAttributeList) => {
// Check if a regular expression validates the attribute.
return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
- .every(regex => regex.test(attributeName))
+ .some(regex => regex.test(attributeName))
}
export const DefaultAllowlist = {
diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js
index f9a2d992d..55b7244ab 100644
--- a/js/src/util/scrollbar.js
+++ b/js/src/util/scrollbar.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.2): util/scrollBar.js
+ * Bootstrap (v5.1.3): util/scrollBar.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -9,14 +9,23 @@ import SelectorEngine from '../dom/selector-engine'
import Manipulator from '../dom/manipulator'
import { isElement } from './index'
+/**
+ * Constants
+ */
+
const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
const SELECTOR_STICKY_CONTENT = '.sticky-top'
+/**
+ * Class definition
+ */
+
class ScrollBarHelper {
constructor() {
this._element = document.body
}
+ // Public
getWidth() {
// https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
const documentWidth = document.documentElement.clientWidth
@@ -33,6 +42,18 @@ class ScrollBarHelper {
this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)
}
+ reset() {
+ this._resetElementAttributes(this._element, 'overflow')
+ this._resetElementAttributes(this._element, 'paddingRight')
+ this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')
+ this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')
+ }
+
+ isOverflowing() {
+ return this.getWidth() > 0
+ }
+
+ // Private
_disableOverFlow() {
this._saveInitialAttribute(this._element, 'overflow')
this._element.style.overflow = 'hidden'
@@ -53,13 +74,6 @@ class ScrollBarHelper {
this._applyManipulationCallback(selector, manipulationCallBack)
}
- reset() {
- this._resetElementAttributes(this._element, 'overflow')
- this._resetElementAttributes(this._element, 'paddingRight')
- this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')
- this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')
- }
-
_saveInitialAttribute(element, styleProp) {
const actualValue = element.style[styleProp]
if (actualValue) {
@@ -90,10 +104,6 @@ class ScrollBarHelper {
}
}
}
-
- isOverflowing() {
- return this.getWidth() > 0
- }
}
export default ScrollBarHelper
diff --git a/js/src/util/swipe.js b/js/src/util/swipe.js
new file mode 100644
index 000000000..87a5f7f5a
--- /dev/null
+++ b/js/src/util/swipe.js
@@ -0,0 +1,140 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v5.1.3): util/swipe.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+import EventHandler from '../dom/event-handler'
+import { execute, typeCheckConfig } from './index'
+
+/**
+ * Constants
+ */
+
+const NAME = 'swipe'
+const EVENT_KEY = '.bs.swipe'
+const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
+const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
+const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
+const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
+const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
+const POINTER_TYPE_TOUCH = 'touch'
+const POINTER_TYPE_PEN = 'pen'
+const CLASS_NAME_POINTER_EVENT = 'pointer-event'
+const SWIPE_THRESHOLD = 40
+
+const Default = {
+ leftCallback: null,
+ rightCallback: null,
+ endCallback: null
+}
+
+const DefaultType = {
+ leftCallback: '(function|null)',
+ rightCallback: '(function|null)',
+ endCallback: '(function|null)'
+}
+
+/**
+ * Class definition
+ */
+
+class Swipe {
+ constructor(element, config) {
+ this._element = element
+
+ if (!element || !Swipe.isSupported()) {
+ return
+ }
+
+ this._config = this._getConfig(config)
+ this._deltaX = 0
+ this._supportPointerEvents = Boolean(window.PointerEvent)
+ this._initEvents()
+ }
+
+ // Public
+ dispose() {
+ EventHandler.off(this._element, EVENT_KEY)
+ }
+
+ // Private
+ _start(event) {
+ if (!this._supportPointerEvents) {
+ this._deltaX = event.touches[0].clientX
+
+ return
+ }
+
+ if (this._eventIsPointerPenTouch(event)) {
+ this._deltaX = event.clientX
+ }
+ }
+
+ _end(event) {
+ if (this._eventIsPointerPenTouch(event)) {
+ this._deltaX = event.clientX - this._deltaX
+ }
+
+ this._handleSwipe()
+ execute(this._config.endCallback)
+ }
+
+ _move(event) {
+ this._deltaX = event.touches && event.touches.length > 1 ?
+ 0 :
+ event.touches[0].clientX - this._deltaX
+ }
+
+ _handleSwipe() {
+ const absDeltaX = Math.abs(this._deltaX)
+
+ if (absDeltaX <= SWIPE_THRESHOLD) {
+ return
+ }
+
+ const direction = absDeltaX / this._deltaX
+
+ this._deltaX = 0
+
+ if (!direction) {
+ return
+ }
+
+ execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)
+ }
+
+ _initEvents() {
+ if (this._supportPointerEvents) {
+ EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))
+ EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))
+
+ this._element.classList.add(CLASS_NAME_POINTER_EVENT)
+ } else {
+ EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))
+ EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))
+ EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))
+ }
+ }
+
+ _getConfig(config) {
+ config = {
+ ...Default,
+ ...(typeof config === 'object' ? config : {})
+ }
+ typeCheckConfig(NAME, config, DefaultType)
+ return config
+ }
+
+ _eventIsPointerPenTouch(event) {
+ return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)
+ }
+
+ // Static
+ static isSupported() {
+ return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
+ }
+}
+
+export default Swipe
diff --git a/js/src/util/template-factory.js b/js/src/util/template-factory.js
new file mode 100644
index 000000000..a9cee1086
--- /dev/null
+++ b/js/src/util/template-factory.js
@@ -0,0 +1,161 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v5.1.3): util/template-factory.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+import { DefaultAllowlist, sanitizeHtml } from './sanitizer'
+import { getElement, isElement, typeCheckConfig } from '../util/index'
+import SelectorEngine from '../dom/selector-engine'
+
+/**
+ * Constants
+ */
+
+const NAME = 'TemplateFactory'
+
+const Default = {
+ extraClass: '',
+ template: '<div></div>',
+ content: {}, // { selector : text , selector2 : text2 , }
+ html: false,
+ sanitize: true,
+ sanitizeFn: null,
+ allowList: DefaultAllowlist
+}
+
+const DefaultType = {
+ extraClass: '(string|function)',
+ template: 'string',
+ content: 'object',
+ html: 'boolean',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ allowList: 'object'
+}
+
+const DefaultContentType = {
+ selector: '(string|element)',
+ entry: '(string|element|function|null)'
+}
+
+/**
+ * Class definition
+ */
+
+class TemplateFactory {
+ constructor(config) {
+ this._config = this._getConfig(config)
+ }
+
+ // Getters
+ static get NAME() {
+ return NAME
+ }
+
+ static get Default() {
+ return Default
+ }
+
+ // Public
+ getContent() {
+ return Object.values(this._config.content)
+ .map(config => this._resolvePossibleFunction(config))
+ .filter(Boolean)
+ }
+
+ hasContent() {
+ return this.getContent().length > 0
+ }
+
+ changeContent(content) {
+ this._checkContent(content)
+ this._config.content = { ...this._config.content, ...content }
+ return this
+ }
+
+ toHtml() {
+ const templateWrapper = document.createElement('div')
+ templateWrapper.innerHTML = this._maybeSanitize(this._config.template)
+
+ for (const [selector, text] of Object.entries(this._config.content)) {
+ this._setContent(templateWrapper, text, selector)
+ }
+
+ const template = templateWrapper.children[0]
+ const extraClass = this._resolvePossibleFunction(this._config.extraClass)
+
+ if (extraClass) {
+ template.classList.add(...extraClass.split(' '))
+ }
+
+ return template
+ }
+
+ // Private
+ _getConfig(config) {
+ config = {
+ ...Default,
+ ...(typeof config === 'object' ? config : {})
+ }
+
+ typeCheckConfig(NAME, config, DefaultType)
+ this._checkContent(config.content)
+
+ return config
+ }
+
+ _checkContent(arg) {
+ for (const [selector, content] of Object.entries(arg)) {
+ typeCheckConfig(NAME, { selector, entry: content }, DefaultContentType)
+ }
+ }
+
+ _setContent(template, content, selector) {
+ const templateElement = SelectorEngine.findOne(selector, template)
+
+ if (!templateElement) {
+ return
+ }
+
+ content = this._resolvePossibleFunction(content)
+
+ if (!content) {
+ templateElement.remove()
+ return
+ }
+
+ if (isElement(content)) {
+ this._putElementInTemplate(getElement(content), templateElement)
+ return
+ }
+
+ if (this._config.html) {
+ templateElement.innerHTML = this._maybeSanitize(content)
+ return
+ }
+
+ templateElement.textContent = content
+ }
+
+ _maybeSanitize(arg) {
+ return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg
+ }
+
+ _resolvePossibleFunction(arg) {
+ return typeof arg === 'function' ? arg(this) : arg
+ }
+
+ _putElementInTemplate(element, templateElement) {
+ if (this._config.html) {
+ templateElement.innerHTML = ''
+ templateElement.append(element)
+ return
+ }
+
+ templateElement.textContent = element.textContent
+ }
+}
+
+export default TemplateFactory