aboutsummaryrefslogtreecommitdiff
path: root/js/src/dom
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/dom')
-rw-r--r--js/src/dom/data.js82
-rw-r--r--js/src/dom/event-handler.js40
-rw-r--r--js/src/dom/manipulator.js6
-rw-r--r--js/src/dom/selector-engine.js19
4 files changed, 86 insertions, 61 deletions
diff --git a/js/src/dom/data.js b/js/src/dom/data.js
index c93a8dc7c..69488b510 100644
--- a/js/src/dom/data.js
+++ b/js/src/dom/data.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.0.0-beta2): dom/data.js
+ * Bootstrap (v5.1.0): dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -11,57 +11,47 @@
* ------------------------------------------------------------------------
*/
-const mapData = (() => {
- const storeData = {}
- let id = 1
- return {
- set(element, key, data) {
- if (typeof element.bsKey === 'undefined') {
- element.bsKey = {
- key,
- id
- }
- id++
- }
+const elementMap = new Map()
- storeData[element.bsKey.id] = data
- },
- get(element, key) {
- if (!element || typeof element.bsKey === 'undefined') {
- return null
- }
-
- const keyProperties = element.bsKey
- if (keyProperties.key === key) {
- return storeData[keyProperties.id]
- }
+export default {
+ set(element, key, instance) {
+ if (!elementMap.has(element)) {
+ elementMap.set(element, new Map())
+ }
- return null
- },
- delete(element, key) {
- if (typeof element.bsKey === 'undefined') {
- return
- }
+ const instanceMap = elementMap.get(element)
- const keyProperties = element.bsKey
- if (keyProperties.key === key) {
- delete storeData[keyProperties.id]
- delete element.bsKey
- }
+ // make it clear we only want one instance per element
+ // can be removed later when multiple key/instances are fine to be used
+ if (!instanceMap.has(key) && instanceMap.size !== 0) {
+ // eslint-disable-next-line no-console
+ console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)
+ return
}
- }
-})()
-const Data = {
- setData(instance, key, data) {
- mapData.set(instance, key, data)
+ instanceMap.set(key, instance)
},
- getData(instance, key) {
- return mapData.get(instance, key)
+
+ get(element, key) {
+ if (elementMap.has(element)) {
+ return elementMap.get(element).get(key) || null
+ }
+
+ return null
},
- removeData(instance, key) {
- mapData.delete(instance, key)
+
+ remove(element, key) {
+ if (!elementMap.has(element)) {
+ return
+ }
+
+ const instanceMap = elementMap.get(element)
+
+ instanceMap.delete(key)
+
+ // free up element references if there are no instances left for an element
+ if (instanceMap.size === 0) {
+ elementMap.delete(element)
+ }
}
}
-
-export default Data
diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js
index 5b11ae3d0..087afde07 100644
--- a/js/src/dom/event-handler.js
+++ b/js/src/dom/event-handler.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.0.0-beta2): dom/event-handler.js
+ * Bootstrap (v5.1.0): dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -22,6 +22,7 @@ const customEvents = {
mouseenter: 'mouseover',
mouseleave: 'mouseout'
}
+const customEventsRegex = /^(mouseenter|mouseleave)/i
const nativeEvents = new Set([
'click',
'dblclick',
@@ -113,7 +114,7 @@ function bootstrapDelegationHandler(element, selector, fn) {
if (handler.oneOff) {
// eslint-disable-next-line unicorn/consistent-destructuring
- EventHandler.off(element, event.type, fn)
+ EventHandler.off(element, event.type, selector, fn)
}
return fn.apply(target, [event])
@@ -144,14 +145,7 @@ function normalizeParams(originalTypeEvent, handler, delegationFn) {
const delegation = typeof handler === 'string'
const originalHandler = delegation ? delegationFn : handler
- // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
- let typeEvent = originalTypeEvent.replace(stripNameRegex, '')
- const custom = customEvents[typeEvent]
-
- if (custom) {
- typeEvent = custom
- }
-
+ let typeEvent = getTypeEvent(originalTypeEvent)
const isNative = nativeEvents.has(typeEvent)
if (!isNative) {
@@ -171,6 +165,24 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
delegationFn = null
}
+ // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
+ // this prevents the handler from being dispatched the same way as mouseover or mouseout does
+ if (customEventsRegex.test(originalTypeEvent)) {
+ const wrapFn = fn => {
+ return function (event) {
+ if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {
+ return fn.call(this, event)
+ }
+ }
+ }
+
+ if (delegationFn) {
+ delegationFn = wrapFn(delegationFn)
+ } else {
+ handler = wrapFn(handler)
+ }
+ }
+
const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
const events = getEvent(element)
const handlers = events[typeEvent] || (events[typeEvent] = {})
@@ -219,6 +231,12 @@ function removeNamespacedHandlers(element, events, typeEvent, namespace) {
})
}
+function getTypeEvent(event) {
+ // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
+ event = event.replace(stripNameRegex, '')
+ return customEvents[event] || event
+}
+
const EventHandler = {
on(element, event, handler, delegationFn) {
addHandler(element, event, handler, delegationFn, false)
@@ -272,7 +290,7 @@ const EventHandler = {
}
const $ = getjQuery()
- const typeEvent = event.replace(stripNameRegex, '')
+ const typeEvent = getTypeEvent(event)
const inNamespace = event !== typeEvent
const isNative = nativeEvents.has(typeEvent)
diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js
index 509797bc0..fdeb69ed8 100644
--- a/js/src/dom/manipulator.js
+++ b/js/src/dom/manipulator.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.0.0-beta2): dom/manipulator.js
+ * Bootstrap (v5.1.0): dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -64,8 +64,8 @@ const Manipulator = {
const rect = element.getBoundingClientRect()
return {
- top: rect.top + document.body.scrollTop,
- left: rect.left + document.body.scrollLeft
+ top: rect.top + window.pageYOffset,
+ left: rect.left + window.pageXOffset
}
},
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index b310098b5..e3988a986 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.0.0-beta2): dom/selector-engine.js
+ * Bootstrap (v5.1.0): dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -11,6 +11,8 @@
* ------------------------------------------------------------------------
*/
+import { isDisabled, isVisible } from '../util/index'
+
const NODE_TEXT = 3
const SelectorEngine = {
@@ -69,6 +71,21 @@ const SelectorEngine = {
}
return []
+ },
+
+ focusableChildren(element) {
+ const focusables = [
+ 'a',
+ 'button',
+ 'input',
+ 'textarea',
+ 'select',
+ 'details',
+ '[tabindex]',
+ '[contenteditable="true"]'
+ ].map(selector => `${selector}:not([tabindex^="-"])`).join(', ')
+
+ return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))
}
}