Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeoSot <geo.sotis@gmail.com>2022-07-04 14:17:59 +0300
committerGeoSot <geo.sotis@gmail.com>2022-08-11 01:27:57 +0300
commit1039830b5bdc75dc1d3eeb8e0f6ed2db8273584b (patch)
tree4a62482aa6e2c4f8b7b3f5180473ac6f4213066b
parentc3c65911665ab64bdaa15d405db65ee81655dbf3 (diff)
initial
-rw-r--r--build/generate-sri.js4
-rw-r--r--build/rollup.config.js6
-rw-r--r--config.yml4
-rw-r--r--js/src/dropdown.js169
-rw-r--r--js/src/popover.js3
-rw-r--r--js/src/tooltip.js161
-rw-r--r--js/src/util/floating-ui.js92
-rw-r--r--package-lock.json29
-rw-r--r--package.json6
-rw-r--r--scss/_dropdown.scss40
-rw-r--r--scss/_popover.scss201
-rw-r--r--scss/_tooltip.scss99
-rw-r--r--scss/_variables.scss1
-rw-r--r--site/assets/js/snippets.js2
-rw-r--r--site/content/docs/5.2/components/dropdowns.md2
-rw-r--r--site/content/docs/5.2/components/popovers.md2
-rw-r--r--site/content/docs/5.2/components/tooltips.md2
-rw-r--r--site/content/docs/5.2/getting-started/download.md2
-rw-r--r--site/content/docs/5.2/getting-started/introduction.md2
-rw-r--r--site/content/docs/5.2/getting-started/javascript.md2
-rw-r--r--site/content/docs/5.2/getting-started/rtl.md2
21 files changed, 346 insertions, 485 deletions
diff --git a/build/generate-sri.js b/build/generate-sri.js
index cde818e090..ac897565f0 100644
--- a/build/generate-sri.js
+++ b/build/generate-sri.js
@@ -42,8 +42,8 @@ const files = [
configPropertyName: 'js_bundle_hash'
},
{
- file: 'node_modules/@popperjs/core/dist/umd/popper.min.js',
- configPropertyName: 'popper_hash'
+ file: 'node_modules/@floating-ui/dom/dist/floating-ui.dom.umd.js',
+ configPropertyName: 'floating_ui_hash'
}
]
diff --git a/build/rollup.config.js b/build/rollup.config.js
index 2d2920fd50..3a9d4d706d 100644
--- a/build/rollup.config.js
+++ b/build/rollup.config.js
@@ -10,7 +10,7 @@ const BUNDLE = process.env.BUNDLE === 'true'
const ESM = process.env.ESM === 'true'
let fileDestination = `bootstrap${ESM ? '.esm' : ''}`
-const external = ['@popperjs/core']
+const external = ['@floating-ui/dom']
const plugins = [
babel({
// Only transpile our source code
@@ -20,14 +20,14 @@ const plugins = [
})
]
const globals = {
- '@popperjs/core': 'Popper'
+ '@floating-ui/dom': 'floatingUi'
}
if (BUNDLE) {
fileDestination += '.bundle'
// Remove last entry in external array to bundle Popper
external.pop()
- delete globals['@popperjs/core']
+ delete globals['@floating-ui/dom']
plugins.push(
replace({
'process.env.NODE_ENV': '"production"',
diff --git a/config.yml b/config.yml
index bb71b8d0ca..62e99f77d4 100644
--- a/config.yml
+++ b/config.yml
@@ -80,8 +80,8 @@ params:
js_hash: "sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK"
js_bundle: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
js_bundle_hash: "sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
- popper: "https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.5/dist/umd/popper.min.js"
- popper_hash: "sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk"
+ floating_ui: "https://cdn.jsdelivr.net/npm/@floating-ui/dom@0.5.4/dist/floating-ui.dom.umd.min.js"
+ floating_ui_hash: "sha384-Os8n9bzoYJ/ESbGD7cW0VOTLk0hO++SO+Y4swXBE2dHrxiZkjADEr5ZGOcc9CorD"
anchors:
min: 2
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 601792953e..a969fa838a 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -5,21 +5,18 @@
* --------------------------------------------------------------------------
*/
-import * as Popper from '@popperjs/core'
+import { inline, offset, shift } from '@floating-ui/dom'
import {
defineJQueryPlugin,
- getElement,
getNextActiveElement,
isDisabled,
- isElement,
- isRTL,
isVisible,
noop
} 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'
+import FloatingUi from './util/floating-ui'
/**
* Constants
@@ -28,7 +25,6 @@ import BaseComponent from './base-component'
const NAME = 'dropdown'
const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
-const DATA_API_KEY = '.data-api'
const ESCAPE_KEY = 'Escape'
const TAB_KEY = 'Tab'
@@ -40,9 +36,9 @@ const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
-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 EVENT_CLICK_DATA_API = `click${EVENT_KEY}`
+const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}`
+const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}`
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
@@ -58,30 +54,28 @@ const SELECTOR_NAVBAR = '.navbar'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
-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 PLACEMENT_TOPCENTER = 'top'
+const PLACEMENT_TOPEND = 'top-end'
+const PLACEMENT_TOP = 'top-start'
const PLACEMENT_BOTTOMCENTER = 'bottom'
+const PLACEMENT_BOTTOMEND = 'bottom-end'
+const PLACEMENT_BOTTOM = 'bottom-start'
+const PLACEMENT_RIGHT = 'right-start'
+const PLACEMENT_LEFT = 'left-start'
const Default = {
autoClose: true,
- boundary: 'clippingParents',
display: 'dynamic',
- offset: [0, 2],
- popperConfig: null,
+ offset: 10,
+ positionConfig: null,
reference: 'toggle'
}
const DefaultType = {
autoClose: '(boolean|string)',
- boundary: '(string|element)',
display: 'string',
- offset: '(array|string|function)',
- popperConfig: '(null|object|function)',
+ offset: '(number|array|string|function)',
+ positionConfig: '(null|object|function)',
reference: '(string|element|object)'
}
@@ -93,10 +87,9 @@ class Dropdown extends BaseComponent {
constructor(element, config) {
super(element, config)
- this._popper = null
this._parent = this._element.parentNode // dropdown wrapper
this._menu = SelectorEngine.findOne(SELECTOR_MENU, this._parent)
- this._inNavbar = this._detectNavbar()
+ this._positionHelper = new FloatingUi(this._element)
}
// Getters
@@ -132,7 +125,7 @@ class Dropdown extends BaseComponent {
return
}
- this._createPopper()
+ this.update()
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
@@ -164,19 +157,9 @@ class Dropdown extends BaseComponent {
this._completeHide(relatedTarget)
}
- dispose() {
- if (this._popper) {
- this._popper.destroy()
- }
-
- super.dispose()
- }
-
update() {
- this._inNavbar = this._detectNavbar()
- if (this._popper) {
- this._popper.update()
- }
+ const reference = this._positionHelper.getReferenceElement(this._config.reference, this._parent, NAME)
+ this._positionHelper.calculate(reference, this._menu, this._getFloatingUiConfig())
}
// Private
@@ -194,47 +177,28 @@ class Dropdown extends BaseComponent {
}
}
- 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')
+ this._positionHelper.stop()
EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)
}
- _getConfig(config) {
- config = super._getConfig(config)
-
- 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.`)
+ _getFloatingUiConfig() {
+ const defaultBsConfig = {
+ placement: this._getPlacement(),
+ middleware: [offset(this._positionHelper.parseOffset(this._config.offset)), shift()]
}
- return config
- }
-
- _createPopper() {
- if (typeof Popper === 'undefined') {
- throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
+ // Disable Popper if we have a static display or Dropdown is in Navbar
+ if (this._detectNavbar() || this._config.display === 'static') {
+ defaultBsConfig.middleware.push(inline())
}
- let referenceElement = this._element
-
- if (this._config.reference === 'parent') {
- referenceElement = this._parent
- } else if (isElement(this._config.reference)) {
- referenceElement = getElement(this._config.reference)
- } else if (typeof this._config.reference === 'object') {
- referenceElement = this._config.reference
+ return {
+ ...defaultBsConfig,
+ ...(typeof this._config.positionConfig === 'function' ? this._config.positionConfig(defaultBsConfig) : this._config.positionConfig)
}
-
- const popperConfig = this._getPopperConfig()
- this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
}
_isShown() {
@@ -244,20 +208,15 @@ class Dropdown extends BaseComponent {
_getPlacement() {
const parentDropdown = this._parent
- if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
- return PLACEMENT_RIGHT
+ const matches = {
+ [CLASS_NAME_DROPEND]: PLACEMENT_RIGHT,
+ [CLASS_NAME_DROPSTART]: PLACEMENT_LEFT,
+ [CLASS_NAME_DROPUP_CENTER]: PLACEMENT_TOPCENTER,
+ [CLASS_NAME_DROPDOWN_CENTER]: PLACEMENT_BOTTOMCENTER
}
-
- if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
- return PLACEMENT_LEFT
- }
-
- if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {
- return PLACEMENT_TOPCENTER
- }
-
- if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {
- return PLACEMENT_BOTTOMCENTER
+ const match = Object.keys(matches).find(keyClass => parentDropdown.classList.contains(keyClass))
+ if (match) {
+ return matches[match]
}
// We need to trim the value because custom properties can also include spaces
@@ -274,52 +233,6 @@ class Dropdown extends BaseComponent {
return this._element.closest(SELECTOR_NAVBAR) !== null
}
- _getOffset() {
- const { offset } = this._config
-
- if (typeof offset === 'string') {
- return offset.split(',').map(value => Number.parseInt(value, 10))
- }
-
- if (typeof offset === 'function') {
- return popperData => offset(popperData, this._element)
- }
-
- return offset
- }
-
- _getPopperConfig() {
- const defaultBsPopperConfig = {
- placement: this._getPlacement(),
- modifiers: [{
- name: 'preventOverflow',
- options: {
- boundary: this._config.boundary
- }
- },
- {
- name: 'offset',
- options: {
- offset: this._getOffset()
- }
- }]
- }
-
- // Disable Popper if we have a static display or Dropdown is in Navbar
- if (this._inNavbar || this._config.display === 'static') {
- Manipulator.setDataAttribute(this._menu, 'popper', 'static') // todo:v6 remove
- defaultBsPopperConfig.modifiers = [{
- name: 'applyStyles',
- enabled: false
- }]
- }
-
- return {
- ...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
- }
- }
-
_selectMenuItem({ key, target }) {
const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))
@@ -364,11 +277,7 @@ class Dropdown extends BaseComponent {
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)
- ) {
+ if (composedPath.includes(context._element) || (context._config.autoClose === 'inside' && !isMenuTarget) || (context._config.autoClose === 'outside' && isMenuTarget)) {
continue
}
diff --git a/js/src/popover.js b/js/src/popover.js
index acfd1ac791..c82cd0bc4d 100644
--- a/js/src/popover.js
+++ b/js/src/popover.js
@@ -23,9 +23,10 @@ const Default = {
offset: [0, 8],
placement: 'right',
template: '<div class="popover" role="tooltip">' +
- '<div class="popover-arrow"></div>' +
+ '<div class="popover-inner">' +
'<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div>' +
+ '</div>' +
'</div>',
trigger: 'click'
}
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index 2c5f03a293..dfd973767b 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -5,13 +5,14 @@
* --------------------------------------------------------------------------
*/
-import * as Popper from '@popperjs/core'
-import { defineJQueryPlugin, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index'
+import { defineJQueryPlugin, findShadowRoot, getElement, getUID, isVisible, noop } from './util/index'
import { DefaultAllowlist } from './util/sanitizer'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import BaseComponent from './base-component'
import TemplateFactory from './util/template-factory'
+import FloatingUi from './util/floating-ui'
+import { flip, hide, offset, shift } from '@floating-ui/dom'
/**
* Constants
@@ -48,9 +49,9 @@ const EVENT_MOUSELEAVE = 'mouseleave'
const AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
- RIGHT: isRTL() ? 'left' : 'right',
+ RIGHT: 'right',
BOTTOM: 'bottom',
- LEFT: isRTL() ? 'right' : 'left'
+ LEFT: 'left'
}
const Default = {
@@ -62,16 +63,15 @@ const Default = {
delay: 0,
fallbackPlacements: ['top', 'right', 'bottom', 'left'],
html: false,
- offset: [0, 0],
+ offset: 0,
placement: 'top',
- popperConfig: null,
+ positionConfig: null,
sanitize: true,
sanitizeFn: null,
selector: false,
template: '<div class="tooltip" role="tooltip">' +
- '<div class="tooltip-arrow"></div>' +
- '<div class="tooltip-inner"></div>' +
- '</div>',
+ '<div class="tooltip-inner"></div>' +
+ '</div>',
title: '',
trigger: 'hover focus'
}
@@ -85,9 +85,9 @@ const DefaultType = {
delay: '(number|object)',
fallbackPlacements: 'array',
html: 'boolean',
- offset: '(array|string|function)',
+ offset: '(number|array|string|function)',
placement: '(string|function)',
- popperConfig: '(null|object|function)',
+ positionConfig: '(null|object|function)',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
selector: '(string|boolean)',
@@ -102,10 +102,6 @@ const DefaultType = {
class Tooltip extends BaseComponent {
constructor(element, config) {
- if (typeof Popper === 'undefined') {
- throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
- }
-
super(element, config)
// Private
@@ -113,7 +109,7 @@ class Tooltip extends BaseComponent {
this._timeout = 0
this._isHovered = false
this._activeTrigger = {}
- this._popper = null
+ this._positionHelper = new FloatingUi(this._element)
this._templateFactory = null
this._newContent = null
@@ -194,7 +190,7 @@ class Tooltip extends BaseComponent {
}
show() {
- if (this._element.style.display === 'none') {
+ if (!isVisible(this._element)) {
throw new Error('Please use show on visible elements')
}
@@ -216,24 +212,9 @@ class Tooltip extends BaseComponent {
this.tip = null
}
- const tip = this._getTipElement()
-
- this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
-
- const { container } = this._config
-
- if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
- container.append(tip)
- EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))
- }
+ this.update()
- if (this._popper) {
- this._popper.update()
- } else {
- this._popper = this._createPopper(tip)
- }
-
- tip.classList.add(CLASS_NAME_SHOW)
+ this.tip.classList.add(CLASS_NAME_SHOW)
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
@@ -291,24 +272,17 @@ class Tooltip extends BaseComponent {
}
if (!this._isHovered) {
+ this._positionHelper.stop()
tip.remove()
}
this._element.removeAttribute('aria-describedby')
EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))
-
- this._disposePopper()
}
this._queueCallback(complete, this.tip, this._isAnimated())
}
- update() {
- if (this._popper) {
- this._popper.update()
- }
- }
-
// Protected
_isWithContent() {
return Boolean(this._getTitle())
@@ -317,6 +291,14 @@ class Tooltip extends BaseComponent {
_getTipElement() {
if (!this.tip) {
this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())
+ this._element.setAttribute('aria-describedby', this.tip.getAttribute('id'))
+ }
+
+ const { container } = this._config
+
+ if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
+ container.append(this.tip)
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))
}
return this.tip
@@ -331,8 +313,6 @@ class Tooltip extends BaseComponent {
}
tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
- // todo: on v6 the following can be achieved with CSS only
- tip.classList.add(`bs-${this.constructor.NAME}-auto`)
const tipId = getUID(this.constructor.NAME).toString()
@@ -392,79 +372,39 @@ class Tooltip extends BaseComponent {
return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)
}
- _createPopper(tip) {
- const placement = typeof this._config.placement === 'function' ?
- this._config.placement.call(this, tip, this._element) :
- this._config.placement
- const attachment = AttachmentMap[placement.toUpperCase()]
- return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
- }
-
- _getOffset() {
- const { offset } = this._config
-
- if (typeof offset === 'string') {
- return offset.split(',').map(value => Number.parseInt(value, 10))
+ update() {
+ this._positionHelper.calculate(this._element, this._getTipElement(), this._getFloatingUiConfig(), { position: 'fixed' })
+ }
+
+ _getFloatingUiConfig() {
+ const defaultBsConfig = {
+ strategy: 'fixed',
+ placement: this._getPlacement(),
+ middleware: [
+ offset(this._positionHelper.parseOffset(this._config.offset)),
+ flip({ fallbackPlacements: this._config.fallbackPlacements }),
+ shift(),
+ hide()
+ ]
}
- if (typeof offset === 'function') {
- return popperData => offset(popperData, this._element)
+ return {
+ ...defaultBsConfig,
+ ...(typeof this._config.positionConfig === 'function' ? this._config.positionConfig(defaultBsConfig) : this._config.positionConfig)
}
+ }
- return offset
+ _getPlacement() {
+ const placement = typeof this._config.placement === 'function' ?
+ this._config.placement.call(this, this.tip, this._element) :
+ this._config.placement
+ return AttachmentMap[placement.toUpperCase()]
}
_resolvePossibleFunction(arg) {
return typeof arg === 'function' ? arg.call(this._element) : arg
}
- _getPopperConfig(attachment) {
- const defaultBsPopperConfig = {
- placement: attachment,
- modifiers: [
- {
- name: 'flip',
- options: {
- fallbackPlacements: this._config.fallbackPlacements
- }
- },
- {
- name: 'offset',
- options: {
- offset: this._getOffset()
- }
- },
- {
- name: 'preventOverflow',
- options: {
- boundary: this._config.boundary
- }
- },
- {
- name: 'arrow',
- options: {
- element: `.${this.constructor.NAME}-arrow`
- }
- },
- {
- name: 'preSetPlacement',
- enabled: true,
- phase: 'beforeMain',
- fn: data => {
- // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
- // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
- this._getTipElement().setAttribute('data-popper-placement', data.state.placement)
- }
- }
- ]
- }
-
- return {
- ...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
- }
- }
-
_setListeners() {
const triggers = this._config.trigger.split(' ')
@@ -621,13 +561,6 @@ class Tooltip extends BaseComponent {
return config
}
- _disposePopper() {
- if (this._popper) {
- this._popper.destroy()
- this._popper = null
- }
- }
-
// Static
static jQueryInterface(config) {
return this.each(function () {
diff --git a/js/src/util/floating-ui.js b/js/src/util/floating-ui.js
new file mode 100644
index 0000000000..5501daf856
--- /dev/null
+++ b/js/src/util/floating-ui.js
@@ -0,0 +1,92 @@
+// import {computePosition, flip, shift} from '@floating-ui/dom'
+//
+//
+import { autoUpdate, computePosition } from '@floating-ui/dom'
+import { getElement, isElement } from './index'
+import Manipulator from '../dom/manipulator'
+
+class FloatingUi {
+ constructor(element) {
+ if (typeof computePosition === 'undefined') {
+ throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
+ }
+
+ this._element = element
+ this._cleanup = null
+ }
+
+ calculate(reference, floatingEl, config, extraCss = {}) {
+ this._cleanup = autoUpdate(reference, floatingEl, () => {
+ computePosition(reference, floatingEl, config)
+ .then(({ x, y, placement, middlewareData }) => {
+ const positionCss = {
+ left: `${x}px`,
+ top: `${y}px`
+ }
+ console.log(middlewareData) // eslint-disable-line no-console
+ if (middlewareData.hide) {
+ const { referenceHidden } = middlewareData.hide
+
+ Object.assign(floatingEl.style, {
+ visibility: referenceHidden ? 'hidden' : 'visible'
+ })
+ }
+
+ Object.assign(floatingEl.style, { ...positionCss, ...extraCss })
+ Manipulator.setDataAttribute(floatingEl, 'placement', placement)
+ })
+ })
+ }
+
+ stop() {
+ if (this._cleanup) {
+ this._cleanup()
+ }
+ }
+
+ getReferenceElement(reference, parent, PluginName) {
+ if (reference === 'parent') {
+ return parent
+ }
+
+ if (isElement(reference)) {
+ return getElement(reference)
+ }
+
+ if (typeof reference === 'object') {
+ if (typeof reference.getBoundingClientRect !== 'function') {
+ throw new TypeError(`${PluginName.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`)
+ }
+
+ return reference
+ }
+
+ return this._element
+ }
+
+ parseOffset(value) {
+ console.log(value) // eslint-disable-line no-console
+ if (typeof value === 'function') {
+ return popperData => value(popperData, this._element)
+ }
+
+ if (typeof value === 'string') {
+ console.log('offset', value) // eslint-disable-line no-console
+ value = [
+ Number.parseInt(value.split(',')[0], 10),
+ Number.parseInt(value.split(',')[1] || 0, 10)
+ ]
+ }
+
+ if (Array.isArray(value)) {
+ return {
+ mainAxis: value[0],
+ alignmentAxis: value[1]
+ }
+ }
+
+ return value
+ }
+}
+
+export default FloatingUi
diff --git a/package-lock.json b/package-lock.json
index b2511343d0..4fd170f487 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,9 @@
}
],
"license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^0.5.4"
+ },
"devDependencies": {
"@babel/cli": "^7.18.10",
"@babel/core": "^7.18.10",
@@ -1770,6 +1773,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
+ "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg=="
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz",
+ "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==",
+ "dependencies": {
+ "@floating-ui/core": "^0.7.3"
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
@@ -11705,6 +11721,19 @@
}
}
},
+ "@floating-ui/core": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
+ "integrity": "sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg=="
+ },
+ "@floating-ui/dom": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz",
+ "integrity": "sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==",
+ "requires": {
+ "@floating-ui/core": "^0.7.3"
+ }
+ },
"@humanwhocodes/config-array": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
diff --git a/package.json b/package.json
index 32ec47f86b..2cabf2a3cf 100644
--- a/package.json
+++ b/package.json
@@ -101,13 +101,13 @@
"watch-js-docs": "nodemon --watch site/assets/js/ --ext js --exec \"npm run js-lint\""
},
"peerDependencies": {
- "@popperjs/core": "^2.11.5"
+ "@floating-ui/dom": "^0.5.4"
},
"devDependencies": {
"@babel/cli": "^7.18.10",
"@babel/core": "^7.18.10",
"@babel/preset-env": "^7.18.10",
- "@popperjs/core": "^2.11.5",
+ "@floating-ui/dom": "^0.5.4",
"@rollup/plugin-babel": "^5.3.1",
"@rollup/plugin-commonjs": "^22.0.1",
"@rollup/plugin-node-resolve": "^13.3.0",
@@ -174,7 +174,7 @@
},
"dependencies": {},
"peerDependencies": {
- "@popperjs/core": "^2.11.5"
+ "@floating-ui/dom": "^0.5.4"
}
}
}
diff --git a/scss/_dropdown.scss b/scss/_dropdown.scss
index 62125b9678..de96a9e1bc 100644
--- a/scss/_dropdown.scss
+++ b/scss/_dropdown.scss
@@ -61,12 +61,6 @@
@include border-radius(var(--#{$prefix}dropdown-border-radius));
@include box-shadow(var(--#{$prefix}dropdown-box-shadow));
- &[data-bs-popper] {
- top: 100%;
- left: 0;
- margin-top: var(--#{$prefix}dropdown-spacer);
- }
-
@if $dropdown-padding-y == 0 {
> .dropdown-item:first-child,
> li:first-child .dropdown-item {
@@ -82,7 +76,7 @@
// scss-docs-start responsive-breakpoints
// We deliberately hardcode the `bs-` prefix because we check
-// this custom property in JS to determine Popper's positioning
+// this custom property in JS to determine positioning
@each $breakpoint in map-keys($grid-breakpoints) {
@include media-breakpoint-up($breakpoint) {
@@ -90,20 +84,10 @@
.dropdown-menu#{$infix}-start {
--bs-position: start;
-
- &[data-bs-popper] {
- right: auto;
- left: 0;
- }
}
.dropdown-menu#{$infix}-end {
--bs-position: end;
-
- &[data-bs-popper] {
- right: 0;
- left: auto;
- }
}
}
}
@@ -112,26 +96,12 @@
// Allow for dropdowns to go bottom up (aka, dropup-menu)
// Just add .dropup after the standard .dropdown class and you're set.
.dropup {
- .dropdown-menu[data-bs-popper] {
- top: auto;
- bottom: 100%;
- margin-top: 0;
- margin-bottom: var(--#{$prefix}dropdown-spacer);
- }
-
.dropdown-toggle {
@include caret(up);
}
}
.dropend {
- .dropdown-menu[data-bs-popper] {
- top: 0;
- right: auto;
- left: 100%;
- margin-top: 0;
- margin-left: var(--#{$prefix}dropdown-spacer);
- }
.dropdown-toggle {
@include caret(end);
@@ -142,14 +112,6 @@
}
.dropstart {
- .dropdown-menu[data-bs-popper] {
- top: 0;
- right: 100%;
- left: auto;
- margin-top: 0;
- margin-right: var(--#{$prefix}dropdown-spacer);
- }
-
.dropdown-toggle {
@include caret(start);
&::before {
diff --git a/scss/_popover.scss b/scss/_popover.scss
index b00c02959d..eb13efd12f 100644
--- a/scss/_popover.scss
+++ b/scss/_popover.scss
@@ -22,6 +22,9 @@
--#{$prefix}popover-arrow-border: var(--#{$prefix}popover-border-color);
// scss-docs-end popover-css-vars
+ position: absolute;
+ top: 0;
+ left: 0;
z-index: var(--#{$prefix}popover-zindex);
display: block;
max-width: var(--#{$prefix}popover-max-width);
@@ -31,150 +34,122 @@
@include font-size(var(--#{$prefix}popover-font-size));
// Allow breaking very long words so they don't overflow the popover's bounds
word-wrap: break-word;
+
+ &::before,
+ &::after {
+ position: absolute;
+ display: block;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ border-width: 0;
+ transform: translateX(-50%);
+ }
+}
+
+.popover-inner {
+ margin: var(--#{$prefix}popover-arrow-height);
background-color: var(--#{$prefix}popover-bg);
background-clip: padding-box;
border: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-border-color);
@include border-radius(var(--#{$prefix}popover-border-radius));
@include box-shadow(var(--#{$prefix}popover-box-shadow));
-
- .popover-arrow {
- display: block;
- width: var(--#{$prefix}popover-arrow-width);
- height: var(--#{$prefix}popover-arrow-height);
-
- &::before,
- &::after {
- position: absolute;
- display: block;
- content: "";
- border-color: transparent;
- border-style: solid;
- border-width: 0;
- }
- }
}
-.bs-popover-top {
- > .popover-arrow {
- bottom: calc((var(--#{$prefix}popover-arrow-height) * -1) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
-
- &::before,
- &::after {
- border-width: var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
- }
+.popover[data-bs-placement="top"] {
+ &::before,
+ &::after {
+ left: 50%;
+ border-width: var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ }
- &::before {
- bottom: 0;
- border-top-color: var(--#{$prefix}popover-arrow-border);
- }
+ &::before {
+ bottom: 0;
+ border-top-color: var(--#{$prefix}popover-arrow-border);
+ }
- &::after {
- bottom: var(--#{$prefix}popover-border-width);
- border-top-color: var(--#{$prefix}popover-bg);
- }
+ &::after {
+ bottom: var(--#{$prefix}popover-border-width);
+ border-top-color: var(--#{$prefix}popover-bg);
}
}
-/* rtl:begin:ignore */
-.bs-popover-end {
- > .popover-arrow {
- left: calc((var(--#{$prefix}popover-arrow-height) * -1) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
- width: var(--#{$prefix}popover-arrow-height);
- height: var(--#{$prefix}popover-arrow-width);
-
- &::before,
- &::after {
- border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
- }
-
- &::before {
- left: 0;
- border-right-color: var(--#{$prefix}popover-arrow-border);
- }
-
- &::after {
- left: var(--#{$prefix}popover-border-width);
- border-right-color: var(--#{$prefix}popover-bg);
- }
+.popover[data-bs-placement="bottom"] {
+ &::before,
+ &::after {
+ left: 50%;
+ border-width: 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list
}
-}
-/* rtl:end:ignore */
+ &::before {
+ top: 0;
+ border-bottom-color: var(--#{$prefix}popover-arrow-border);
+ }
-.bs-popover-bottom {
- > .popover-arrow {
- top: calc((var(--#{$prefix}popover-arrow-height) * -1) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
+ &::after {
+ top: var(--#{$prefix}popover-border-width);
+ border-bottom-color: var(--#{$prefix}popover-bg);
+ }
+}
- &::before,
- &::after {
- border-width: 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list
- }
- &::before {
- top: 0;
- border-bottom-color: var(--#{$prefix}popover-arrow-border);
- }
+/* rtl:begin:ignore */
- &::after {
- top: var(--#{$prefix}popover-border-width);
- border-bottom-color: var(--#{$prefix}popover-bg);
- }
+.popover[data-bs-placement="right"] {
+ &::before,
+ &::after {
+ top: 50%;
+ border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height) calc(var(--#{$prefix}popover-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ transform: translateY(-50%);
}
- // This will remove the popover-header's border just below the arrow
- .popover-header::before {
- position: absolute;
- top: 0;
- left: 50%;
- display: block;
- width: var(--#{$prefix}popover-arrow-width);
- margin-left: calc(var(--#{$prefix}popover-arrow-width) * -.5); // stylelint-disable-line function-disallowed-list
- content: "";
- border-bottom: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-header-bg);
+ &::before {
+ left: 0;
+ border-right-color: var(--#{$prefix}popover-arrow-border);
}
-}
-/* rtl:begin:ignore */
-.bs-popover-start {
- > .popover-arrow {
- right: calc((var(--#{$prefix}popover-arrow-height) * -1) - var(--#{$prefix}popover-border-width)); // stylelint-disable-line function-disallowed-list
- width: var(--#{$prefix}popover-arrow-height);
- height: var(--#{$prefix}popover-arrow-width);
-
- &::before,
- &::after {
- border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list
- }
-
- &::before {
- right: 0;
- border-left-color: var(--#{$prefix}popover-arrow-border);
- }
-
- &::after {
- right: var(--#{$prefix}popover-border-width);
- border-left-color: var(--#{$prefix}popover-bg);
- }
+ &::after {
+ left: var(--#{$prefix}popover-border-width);
+ border-right-color: var(--#{$prefix}popover-bg);
}
}
-/* rtl:end:ignore */
-.bs-popover-auto {
- &[data-popper-placement^="top"] {
- @extend .bs-popover-top;
+.popover[data-bs-placement="left"] {
+ &::before,
+ &::after {
+ top: 50%;
+ border-width: calc(var(--#{$prefix}popover-arrow-width) * .5) 0 calc(var(--#{$prefix}popover-arrow-width) * .5) var(--#{$prefix}popover-arrow-height); // stylelint-disable-line function-disallowed-list
+ transform: translateY(-50%);
}
- &[data-popper-placement^="right"] {
- @extend .bs-popover-end;
- }
- &[data-popper-placement^="bottom"] {
- @extend .bs-popover-bottom;
+
+ &::before {
+ right: 0;
+ border-left-color: var(--#{$prefix}popover-arrow-border);
}
- &[data-popper-placement^="left"] {
- @extend .bs-popover-start;
+
+ &::after {
+ right: var(--#{$prefix}popover-border-width);
+ border-left-color: var(--#{$prefix}popover-bg);
}
}
+
+/* rtl:end:ignore */
+
+// This will remove the popover-header's border just below the arrow
+.popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: var(--#{$prefix}popover-arrow-width);
+ margin-left: calc(var(--#{$prefix}popover-arrow-width) * -.5); // stylelint-disable-line function-disallowed-list
+ content: "";
+ border-bottom: var(--#{$prefix}popover-border-width) solid var(--#{$prefix}popover-header-bg);
+}
+
+
// Offset the popover to account for the popover arrow
.popover-header {
padding: var(--#{$prefix}popover-header-padding-y) var(--#{$prefix}popover-header-padding-x);
diff --git a/scss/_tooltip.scss b/scss/_tooltip.scss
index 7da3df3e00..8aae09528e 100644
--- a/scss/_tooltip.scss
+++ b/scss/_tooltip.scss
@@ -5,7 +5,6 @@
--#{$prefix}tooltip-max-width: #{$tooltip-max-width};
--#{$prefix}tooltip-padding-x: #{$tooltip-padding-x};
--#{$prefix}tooltip-padding-y: #{$tooltip-padding-y};
- --#{$prefix}tooltip-margin: #{$tooltip-margin};
@include rfs($tooltip-font-size, --#{$prefix}tooltip-font-size);
--#{$prefix}tooltip-color: #{$tooltip-color};
--#{$prefix}tooltip-bg: #{$tooltip-bg};
@@ -15,11 +14,12 @@
--#{$prefix}tooltip-arrow-height: #{$tooltip-arrow-height};
// scss-docs-end tooltip-css-vars
+ position: absolute;
+ top: 0;
+ left: 0;
z-index: var(--#{$prefix}tooltip-zindex);
display: block;
padding: var(--#{$prefix}tooltip-arrow-height);
- margin: var(--#{$prefix}tooltip-margin);
- @include deprecate("`$tooltip-margin`", "v5", "v5.x", true);
// Our parent element can be arbitrary since tooltips are by default inserted as a sibling of their target element.
// So reset our font and text properties to avoid inheriting weird values.
@include reset-text();
@@ -30,83 +30,44 @@
&.show { opacity: var(--#{$prefix}tooltip-opacity); }
- .tooltip-arrow {
- display: block;
- width: var(--#{$prefix}tooltip-arrow-width);
- height: var(--#{$prefix}tooltip-arrow-height);
-
- &::before {
- position: absolute;
- content: "";
- border-color: transparent;
- border-style: solid;
- }
- }
-}
-
-.bs-tooltip-top .tooltip-arrow {
- bottom: 0;
-
&::before {
- top: -1px;
- border-width: var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
- border-top-color: var(--#{$prefix}tooltip-bg);
+ position: absolute;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+ transform: translateX(-50%);
}
-}
-
-/* rtl:begin:ignore */
-.bs-tooltip-end .tooltip-arrow {
- left: 0;
- width: var(--#{$prefix}tooltip-arrow-height);
- height: var(--#{$prefix}tooltip-arrow-width);
- &::before {
- right: -1px;
- border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
- border-right-color: var(--#{$prefix}tooltip-bg);
- }
}
-/* rtl:end:ignore */
+.tooltip[data-bs-placement="top"]::before {
+ bottom: 0;
+ left: 50%;
+ border-width: var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ border-top-color: var(--#{$prefix}tooltip-bg);
+}
-.bs-tooltip-bottom .tooltip-arrow {
+.tooltip[data-bs-placement="bottom"]::before {
top: 0;
-
- &::before {
- bottom: -1px;
- border-width: 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
- border-bottom-color: var(--#{$prefix}tooltip-bg);
- }
+ left: 50%;
+ border-width: 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
+ border-bottom-color: var(--#{$prefix}tooltip-bg);
}
-/* rtl:begin:ignore */
-.bs-tooltip-start .tooltip-arrow {
- right: 0;
- width: var(--#{$prefix}tooltip-arrow-height);
- height: var(--#{$prefix}tooltip-arrow-width);
-
- &::before {
- left: -1px;
- border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
- border-left-color: var(--#{$prefix}tooltip-bg);
- }
+.tooltip[data-bs-placement="right"]::before {
+ top: 50%;
+ left: 0;
+ border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height) calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0; // stylelint-disable-line function-disallowed-list
+ border-right-color: var(--#{$prefix}tooltip-bg);
+ transform: translateY(-50%);
}
-/* rtl:end:ignore */
-
-.bs-tooltip-auto {
- &[data-popper-placement^="top"] {
- @extend .bs-tooltip-top;
- }
- &[data-popper-placement^="right"] {
- @extend .bs-tooltip-end;
- }
- &[data-popper-placement^="bottom"] {
- @extend .bs-tooltip-bottom;
- }
- &[data-popper-placement^="left"] {
- @extend .bs-tooltip-start;
- }
+.tooltip[data-bs-placement="left"]::before {
+ top: 50%;
+ right: 0;
+ border-width: calc(var(--#{$prefix}tooltip-arrow-width) * .5) 0 calc(var(--#{$prefix}tooltip-arrow-width) * .5) var(--#{$prefix}tooltip-arrow-height); // stylelint-disable-line function-disallowed-list
+ border-left-color: var(--#{$prefix}tooltip-bg);
+ transform: translateY(-50%);
}
// Wrapper for the tooltip content
diff --git a/scss/_variables.scss b/scss/_variables.scss
index 82d33b0baf..0013865cdc 100644
--- a/scss/_variables.scss
+++ b/scss/_variables.scss
@@ -1299,7 +1299,6 @@ $tooltip-border-radius: $border-radius !default;
$tooltip-opacity: .9 !default;
$tooltip-padding-y: $spacer * .25 !default;
$tooltip-padding-x: $spacer * .5 !default;
-$tooltip-margin: null !default; // TODO: remove this in v6
$tooltip-arrow-width: .8rem !default;
$tooltip-arrow-height: .4rem !default;
diff --git a/site/assets/js/snippets.js b/site/assets/js/snippets.js
index 53f8a786ec..075c5e50ce 100644
--- a/site/assets/js/snippets.js
+++ b/site/assets/js/snippets.js
@@ -23,7 +23,7 @@
// Instantiate all tooltips in a docs or StackBlitz page
document.querySelectorAll('[data-bs-toggle="tooltip"]')
.forEach(tooltip => {
- new bootstrap.Tooltip(tooltip)
+ new bootstrap.Tooltip(tooltip, { trigger: 'click' })
})
// --------
diff --git a/site/content/docs/5.2/components/dropdowns.md b/site/content/docs/5.2/components/dropdowns.md
index ec7e584b5b..7eb31bbe2f 100644
--- a/site/content/docs/5.2/components/dropdowns.md
+++ b/site/content/docs/5.2/components/dropdowns.md
@@ -10,7 +10,7 @@ toc: true
Dropdowns are toggleable, contextual overlays for displaying lists of links and more. They're made interactive with the included Bootstrap dropdown JavaScript plugin. They're toggled by clicking, not by hovering; this is [an intentional design decision](https://markdotto.com/2012/02/27/bootstrap-explained-dropdowns/).
-Dropdowns are built on a third party library, [Popper](https://popper.js.org/), which provides dynamic positioning and viewport detection. Be sure to include [popper.min.js]({{< param "cdn.popper" >}}) before Bootstrap's JavaScript or use `bootstrap.bundle.min.js` / `bootstrap.bundle.js` which contains Popper. Popper isn't used to position dropdowns in navbars though as dynamic positioning isn't required.
+Dropdowns are built on a third party library, [Popper](https://popper.js.org/), which provides dynamic positioning and viewport detection. Be sure to include [popper.min.js]({{< param "cdn.floating_ui" >}}) before Bootstrap's JavaScript or use `bootstrap.bundle.min.js` / `bootstrap.bundle.js` which contains Popper. Popper isn't used to position dropdowns in navbars though as dynamic positioning isn't required.
## Accessibility
diff --git a/site/content/docs/5.2/components/popovers.md b/site/content/docs/5.2/components/popovers.md
index 87e756434b..82953bd9eb 100644
--- a/site/content/docs/5.2/components/popovers.md
+++ b/site/content/docs/5.2/components/popovers.md
@@ -10,7 +10,7 @@ toc: true
Things to know when using the popover plugin:
-- Popovers rely on the third party library [Popper](https://popper.js.org/) for positioning. You must include [popper.min.js]({{< param "cdn.popper" >}}) before `bootstrap.js`, or use one `bootstrap.bundle.min.js` which contains Popper.
+- Popovers rely on the third party library [Popper](https://popper.js.org/) for positioning. You must include [popper.min.js]({{< param "cdn.floating_ui" >}}) before `bootstrap.js`, or use one `bootstrap.bundle.min.js` which contains Popper.
- Popovers require the [popover plugin]({{< docsref "/components/popovers" >}}) as a dependency.
- Popovers are opt-in for performance reasons, so **you must initialize them yourself**.
- Zero-length `title` and `content` values will never show a popover.
diff --git a/site/content/docs/5.2/components/tooltips.md b/site/content/docs/5.2/components/tooltips.md
index 9f1bbdc8da..2c78820d8d 100644
--- a/site/content/docs/5.2/components/tooltips.md
+++ b/site/content/docs/5.2/components/tooltips.md
@@ -10,7 +10,7 @@ toc: true
Things to know when using the tooltip plugin:
-- Tooltips rely on the third party library [Popper](https://popper.js.org/) for positioning. You must include [popper.min.js]({{< param "cdn.popper" >}}) before `bootstrap.js`, or use one `bootstrap.bundle.min.js` which contains Popper.
+- Tooltips rely on the third party library [Popper](https://popper.js.org/) for positioning. You must include [popper.min.js]({{< param "cdn.floating_ui" >}}) before `bootstrap.js`, or use one `bootstrap.bundle.min.js` which contains Popper.
- Tooltips are opt-in for performance reasons, so **you must initialize them yourself**.
- Tooltips with zero-length titles are never displayed.
- Specify `container: 'body'` to avoid rendering problems in more complex components (like our input groups, button groups, etc).
diff --git a/site/content/docs/5.2/getting-started/download.md b/site/content/docs/5.2/getting-started/download.md
index eeffdc83e5..32a75f7c60 100644
--- a/site/content/docs/5.2/getting-started/download.md
+++ b/site/content/docs/5.2/getting-started/download.md
@@ -46,7 +46,7 @@ Skip the download with [jsDelivr](https://www.jsdelivr.com/) to deliver cached v
If you're using our compiled JavaScript and prefer to include Popper separately, add Popper before our JS, via a CDN preferably.
```html
-<script src="{{< param "cdn.popper" >}}" integrity="{{< param "cdn.popper_hash" >}}" crossorigin="anonymous"></script>
+<script src="{{< param "cdn.floating_ui" >}}" integrity="{{< param "cdn.floating_ui_hash" >}}" crossorigin="anonymous"></script>
<script src="{{< param "cdn.js" >}}" integrity="{{< param "cdn.js_hash" >}}" crossorigin="anonymous"></script>
```
diff --git a/site/content/docs/5.2/getting-started/introduction.md b/site/content/docs/5.2/getting-started/introduction.md
index 579e04b2f4..096e533037 100644
--- a/site/content/docs/5.2/getting-started/introduction.md
+++ b/site/content/docs/5.2/getting-started/introduction.md
@@ -53,7 +53,7 @@ Get started by including Bootstrap's production-ready CSS and JavaScript via CDN
You can also include [Popper](https://popper.js.org/) and our JS separately. If you don't plan to use dropdowns, popovers, or tooltips, save some kilobytes by not including Popper.
```html
- <script src="{{< param "cdn.popper" >}}" integrity="{{< param "cdn.popper_hash" >}}" crossorigin="anonymous"></script>
+ <script src="{{< param "cdn.floating_ui" >}}" integrity="{{< param "cdn.floating_ui_hash" >}}" crossorigin="anonymous"></script>
<script src="{{< param "cdn.js" >}}" integrity="{{< param "cdn.js_hash" >}}" crossorigin="anonymous"></script>
```
diff --git a/site/content/docs/5.2/getting-started/javascript.md b/site/content/docs/5.2/getting-started/javascript.md
index fa157e006c..48a1e31148 100644
--- a/site/content/docs/5.2/getting-started/javascript.md
+++ b/site/content/docs/5.2/getting-started/javascript.md
@@ -71,7 +71,7 @@ To fix this, you can use an `importmap` to resolve the arbitrary module names to
<script type="importmap">
{
"imports": {
- "@popperjs/core": "{{< param "cdn.popper" >}}",
+ "@popperjs/core": "{{< param "cdn.floating_ui" >}}",
"bootstrap": "https://cdn.jsdelivr.net/npm/bootstrap@{{< param "current_version" >}}/dist/js/bootstrap.esm.min.js"
}
}
diff --git a/site/content/docs/5.2/getting-started/rtl.md b/site/content/docs/5.2/getting-started/rtl.md
index f4abf050ba..9511e71fb4 100644
--- a/site/content/docs/5.2/getting-started/rtl.md
+++ b/site/content/docs/5.2/getting-started/rtl.md
@@ -58,7 +58,7 @@ You can see the above requirements reflected in this modified RTL starter templa
<!-- Option 2: Separate Popper and Bootstrap JS -->
<!--
- <script src="{{< param "cdn.popper" >}}" integrity="{{< param "cdn.popper_hash" >}}" crossorigin="anonymous"></script>
+ <script src="{{< param "cdn.floating_ui" >}}" integrity="{{< param "cdn.floating_ui_hash" >}}" crossorigin="anonymous"></script>
<script src="{{< param "cdn.js" >}}" integrity="{{< param "cdn.js_hash" >}}" crossorigin="anonymous"></script>
-->
</body>