Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 680b933f68 | |||
| ce6a22159a | |||
| 6f65df4fae | |||
| aedd7fb9de | |||
| 9af1232c65 | |||
| 3bd5756414 | |||
| 4600a25404 | |||
| a1b1e43ddc | |||
| b5ee6694c1 | |||
| d6a38b347f | |||
| 9cee77919d | |||
| 6e55fa9297 | |||
| 74184f8c61 | |||
| bcfc8d0d63 |
@@ -5,6 +5,8 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import Toa from './src/toa'
|
||||
|
||||
export { default as Alert } from './src/alert'
|
||||
export { default as Button } from './src/button'
|
||||
export { default as Carousel } from './src/carousel'
|
||||
@@ -17,3 +19,5 @@ export { default as ScrollSpy } from './src/scrollspy'
|
||||
export { default as Tab } from './src/tab'
|
||||
export { default as Toast } from './src/toast'
|
||||
export { default as Tooltip } from './src/tooltip'
|
||||
|
||||
window.customElements.define('bs-toa', Toa)
|
||||
|
||||
@@ -16,6 +16,7 @@ import Popover from './src/popover'
|
||||
import ScrollSpy from './src/scrollspy'
|
||||
import Tab from './src/tab'
|
||||
import Toast from './src/toast'
|
||||
import Toa from './src/toa'
|
||||
import Tooltip from './src/tooltip'
|
||||
|
||||
export default {
|
||||
@@ -32,3 +33,5 @@ export default {
|
||||
Toast,
|
||||
Tooltip
|
||||
}
|
||||
|
||||
window.customElements.define('bs-toa', Toa)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { executeAfterTransition } from './util/index'
|
||||
import Manipulator from './dom/manipulator'
|
||||
|
||||
class Base extends HTMLElement {
|
||||
constructor(config) {
|
||||
super()
|
||||
|
||||
this._config = this._mergeConfigObj(config, this)
|
||||
}
|
||||
|
||||
_queueCallback(callback, element, isAnimated = true) {
|
||||
executeAfterTransition(callback, element, isAnimated)
|
||||
}
|
||||
|
||||
static get DATA_KEY() {
|
||||
return `bs.${this.NAME}`
|
||||
}
|
||||
|
||||
static get EVENT_KEY() {
|
||||
return `.${this.DATA_KEY}`
|
||||
}
|
||||
|
||||
_mergeConfigObj(config, element) {
|
||||
const jsonConfig = Manipulator.getDataAttribute(element, 'config') // try to parse
|
||||
|
||||
return {
|
||||
...this.constructor.Default,
|
||||
...(typeof jsonConfig === 'object' ? jsonConfig : {}),
|
||||
...Manipulator.getDataAttributes(element),
|
||||
...(typeof config === 'object' ? config : {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Base
|
||||
+2
-2
@@ -223,9 +223,9 @@ class Modal extends BaseComponent {
|
||||
})
|
||||
|
||||
EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {
|
||||
// a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
|
||||
EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {
|
||||
// a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
|
||||
if (this._dialog.contains(event.target) || this._dialog.contains(event2.target)) {
|
||||
if (this._element !== event.target || this._element !== event2.target) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap (v5.2.1): toa.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
import Base from './base'
|
||||
import EventHandler from './dom/event-handler'
|
||||
|
||||
const NAME = 'toast'
|
||||
const DATA_KEY = 'bs.toast'
|
||||
const EVENT_KEY = `.${DATA_KEY}`
|
||||
|
||||
const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`
|
||||
const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`
|
||||
const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
|
||||
const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`
|
||||
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 CLASS_NAME_FADE = 'fade'
|
||||
const CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility
|
||||
const CLASS_NAME_SHOW = 'show'
|
||||
const CLASS_NAME_SHOWING = 'showing'
|
||||
//
|
||||
// const TEMPLATE = '<slot></slot>'
|
||||
|
||||
const DefaultType = {
|
||||
animation: 'boolean',
|
||||
autohide: 'boolean',
|
||||
delay: 'number'
|
||||
}
|
||||
|
||||
const Default = {
|
||||
animation: true,
|
||||
autohide: true,
|
||||
delay: 5000
|
||||
}
|
||||
|
||||
export default class Toa extends Base {
|
||||
constructor() {
|
||||
super()
|
||||
console.log(this._config) // eslint-disable-line no-console
|
||||
this.classList.add('toast', 'p-2')
|
||||
this.setAttribute('role', 'alert')
|
||||
this.setAttribute('aria-live', 'assertlive')
|
||||
this.setAttribute('aria-atomic', 'true')
|
||||
this._timeout = null
|
||||
this._hasMouseInteraction = false
|
||||
this._hasKeyboardInteraction = false
|
||||
}
|
||||
|
||||
static get Default() {
|
||||
return Default
|
||||
}
|
||||
|
||||
static get DefaultType() {
|
||||
return DefaultType
|
||||
}
|
||||
|
||||
static get NAME() {
|
||||
return NAME
|
||||
}
|
||||
|
||||
show() {
|
||||
const showEvent = EventHandler.trigger(this, EVENT_SHOW)
|
||||
|
||||
if (showEvent.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
|
||||
this._clearTimeout()
|
||||
|
||||
if (this._config.animation) {
|
||||
this.classList.add(CLASS_NAME_FADE)
|
||||
}
|
||||
|
||||
const complete = () => {
|
||||
this.classList.remove(CLASS_NAME_SHOWING)
|
||||
EventHandler.trigger(this, EVENT_SHOWN)
|
||||
|
||||
this._maybeScheduleHide()
|
||||
}
|
||||
|
||||
this.classList.remove(CLASS_NAME_HIDE) // @deprecated
|
||||
// reflow(this)
|
||||
this.style.display = 'block'
|
||||
this.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)
|
||||
|
||||
this._queueCallback(complete, this, this._config.animation)
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.isShown()) {
|
||||
return
|
||||
}
|
||||
|
||||
const hideEvent = EventHandler.trigger(this, EVENT_HIDE)
|
||||
|
||||
if (hideEvent.defaultPrevented) {
|
||||
return
|
||||
}
|
||||
|
||||
const complete = () => {
|
||||
this.classList.add(CLASS_NAME_HIDE) // @deprecated
|
||||
this.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)
|
||||
EventHandler.trigger(this, EVENT_HIDDEN)
|
||||
this.style.removeProperty('display')
|
||||
}
|
||||
|
||||
this.classList.add(CLASS_NAME_SHOWING)
|
||||
this._queueCallback(complete, this, this._config.animation)
|
||||
}
|
||||
|
||||
isShown() {
|
||||
return this.classList.contains(CLASS_NAME_SHOW)
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_maybeScheduleHide() {
|
||||
if (!this._config.autohide) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this._hasMouseInteraction || this._hasKeyboardInteraction) {
|
||||
return
|
||||
}
|
||||
|
||||
this._timeout = setTimeout(() => {
|
||||
this.hide()
|
||||
}, this._config.delay)
|
||||
}
|
||||
|
||||
_onInteraction(event, isInteracting) {
|
||||
switch (event.type) {
|
||||
case 'mouseover':
|
||||
case 'mouseout':
|
||||
this._hasMouseInteraction = isInteracting
|
||||
break
|
||||
case 'focusin':
|
||||
case 'focusout':
|
||||
this._hasKeyboardInteraction = isInteracting
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (isInteracting) {
|
||||
this._clearTimeout()
|
||||
return
|
||||
}
|
||||
|
||||
const nextElement = event.relatedTarget
|
||||
if (this === nextElement || this.contains(nextElement)) {
|
||||
return
|
||||
}
|
||||
|
||||
this._maybeScheduleHide()
|
||||
}
|
||||
|
||||
_setListeners() {
|
||||
EventHandler.on(this, EVENT_MOUSEOVER, event => this._onInteraction(event, true))
|
||||
EventHandler.on(this, EVENT_MOUSEOUT, event => this._onInteraction(event, false))
|
||||
EventHandler.on(this, EVENT_FOCUSIN, event => this._onInteraction(event, true))
|
||||
EventHandler.on(this, EVENT_FOCUSOUT, event => this._onInteraction(event, false))
|
||||
}
|
||||
|
||||
_clearTimeout() {
|
||||
clearTimeout(this._timeout)
|
||||
this._timeout = null
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this._setListeners()
|
||||
this.show()
|
||||
}
|
||||
|
||||
//
|
||||
// static get observedAttributes() {
|
||||
// return ['duration'];
|
||||
// }
|
||||
//
|
||||
// attributeChangedCallback(name, oldValue, newValue) {
|
||||
// if (name === 'duration') {
|
||||
// this.duration = newValue;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
+18
-31
@@ -121,6 +121,10 @@ class Tooltip extends BaseComponent {
|
||||
this.tip = null
|
||||
|
||||
this._setListeners()
|
||||
|
||||
if (!this._config.selector) {
|
||||
this._fixTitle()
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
@@ -149,25 +153,12 @@ class Tooltip extends BaseComponent {
|
||||
this._isEnabled = !this._isEnabled
|
||||
}
|
||||
|
||||
toggle(event) {
|
||||
toggle() {
|
||||
if (!this._isEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event) {
|
||||
const context = this._initializeOnDelegatedTarget(event)
|
||||
|
||||
context._activeTrigger.click = !context._activeTrigger.click
|
||||
|
||||
if (context._isWithActiveTrigger()) {
|
||||
context._enter()
|
||||
} else {
|
||||
context._leave()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this._activeTrigger.click = !this._activeTrigger.click
|
||||
if (this._isShown()) {
|
||||
this._leave()
|
||||
return
|
||||
@@ -185,8 +176,8 @@ class Tooltip extends BaseComponent {
|
||||
this.tip.remove()
|
||||
}
|
||||
|
||||
if (this._config.originalTitle) {
|
||||
this._element.setAttribute('title', this._config.originalTitle)
|
||||
if (this._element.getAttribute('data-bs-original-title')) {
|
||||
this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))
|
||||
}
|
||||
|
||||
this._disposePopper()
|
||||
@@ -375,7 +366,7 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
_getTitle() {
|
||||
return this._resolvePossibleFunction(this._config.title) || this._config.originalTitle
|
||||
return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')
|
||||
}
|
||||
|
||||
// Private
|
||||
@@ -469,7 +460,10 @@ class Tooltip extends BaseComponent {
|
||||
|
||||
for (const trigger of triggers) {
|
||||
if (trigger === 'click') {
|
||||
EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => this.toggle(event))
|
||||
EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {
|
||||
const context = this._initializeOnDelegatedTarget(event)
|
||||
context.toggle()
|
||||
})
|
||||
} else if (trigger !== TRIGGER_MANUAL) {
|
||||
const eventIn = trigger === TRIGGER_HOVER ?
|
||||
this.constructor.eventName(EVENT_MOUSEENTER) :
|
||||
@@ -500,20 +494,10 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)
|
||||
|
||||
if (this._config.selector) {
|
||||
this._config = {
|
||||
...this._config,
|
||||
trigger: 'manual',
|
||||
selector: ''
|
||||
}
|
||||
} else {
|
||||
this._fixTitle()
|
||||
}
|
||||
}
|
||||
|
||||
_fixTitle() {
|
||||
const title = this._config.originalTitle
|
||||
const title = this._element.getAttribute('title')
|
||||
|
||||
if (!title) {
|
||||
return
|
||||
@@ -523,6 +507,7 @@ class Tooltip extends BaseComponent {
|
||||
this._element.setAttribute('aria-label', title)
|
||||
}
|
||||
|
||||
this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility
|
||||
this._element.removeAttribute('title')
|
||||
}
|
||||
|
||||
@@ -593,7 +578,6 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
config.originalTitle = this._element.getAttribute('title') || ''
|
||||
if (typeof config.title === 'number') {
|
||||
config.title = config.title.toString()
|
||||
}
|
||||
@@ -614,6 +598,9 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
}
|
||||
|
||||
config.selector = false
|
||||
config.trigger = 'manual'
|
||||
|
||||
// In the future can be replaced with:
|
||||
// const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
|
||||
// `Object.fromEntries(keysWithDifferentValues)`
|
||||
|
||||
@@ -734,6 +734,36 @@ describe('Modal', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should not close modal when clicking on an element removed from modal content', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="modal">',
|
||||
' <div class="modal-dialog">',
|
||||
' <button class="btn">BTN</button>',
|
||||
' </div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const modalEl = fixtureEl.querySelector('.modal')
|
||||
const buttonEl = modalEl.querySelector('.btn')
|
||||
const modal = new Modal(modalEl)
|
||||
|
||||
const spy = spyOn(modal, 'hide')
|
||||
buttonEl.addEventListener('click', () => {
|
||||
buttonEl.remove()
|
||||
})
|
||||
|
||||
modalEl.addEventListener('shown.bs.modal', () => {
|
||||
modalEl.dispatchEvent(createEvent('mousedown'))
|
||||
buttonEl.click()
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
resolve()
|
||||
})
|
||||
|
||||
modal.show()
|
||||
})
|
||||
})
|
||||
|
||||
it('should do nothing is the modal is not shown', () => {
|
||||
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
|
||||
|
||||
|
||||
@@ -66,42 +66,68 @@
|
||||
<div id="shadow" class="pt-5"></div>
|
||||
</div>
|
||||
<div id="customContainer"></div>
|
||||
|
||||
<div class="row mt-4 border-top">
|
||||
<hr>
|
||||
<div class="h4">Test Selector triggered tooltips</div>
|
||||
<div id="wrapperTriggeredBySelector">
|
||||
<div class="py-2 selectorButtonsBlock">
|
||||
<button type="button" class="btn btn-secondary bs-dynamic-tooltip" title="random title">Using title</button>
|
||||
<button type="button" class="btn btn-secondary bs-dynamic-tooltip" data-bs-title="random title">Using bs-title</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<button type="button" class="btn btn-primary" onclick="duplicateButtons()">Duplicate above two buttons</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../../../dist/js/bootstrap.bundle.js"></script>
|
||||
<script>
|
||||
if (typeof document.body.attachShadow === 'function') {
|
||||
var shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' })
|
||||
const shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' })
|
||||
shadowRoot.innerHTML =
|
||||
'<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom">' +
|
||||
'<button id="firstShadowTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom">' +
|
||||
' Tooltip on top in a shadow dom' +
|
||||
'</button>' +
|
||||
'<button id="secondTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom with container option">' +
|
||||
'<button id="secondShadowTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom with container option">' +
|
||||
' Tooltip on top in a shadow dom' +
|
||||
'</button>'
|
||||
|
||||
var firstChildTooltip = new bootstrap.Tooltip(shadowRoot.firstChild)
|
||||
var secondChildTooltip = new bootstrap.Tooltip(shadowRoot.getElementById('secondTooltip'), {
|
||||
new bootstrap.Tooltip(shadowRoot.firstChild)
|
||||
new bootstrap.Tooltip(shadowRoot.getElementById('secondShadowTooltip'), {
|
||||
container: shadowRoot
|
||||
})
|
||||
}
|
||||
|
||||
var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
for (const tooltipEl of tooltipElements) {
|
||||
new bootstrap.Tooltip(tooltipEl)
|
||||
}
|
||||
|
||||
var tooltipElement = document.getElementById('tooltipElement')
|
||||
var tooltipElementInstance = new bootstrap.Tooltip(tooltipElement, {
|
||||
container: document.getElementById('customContainer')
|
||||
new bootstrap.Tooltip('#tooltipElement', {
|
||||
container: '#customContainer'
|
||||
})
|
||||
|
||||
var target = document.getElementById('target')
|
||||
var targetTooltip = new bootstrap.Tooltip(target, {
|
||||
const targetTooltip = new bootstrap.Tooltip('#target', {
|
||||
placement : 'top',
|
||||
trigger : 'manual'
|
||||
})
|
||||
targetTooltip.show()
|
||||
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(tooltipEl=> new bootstrap.Tooltip(tooltipEl))
|
||||
</script>
|
||||
|
||||
<script>
|
||||
new bootstrap.Tooltip('#wrapperTriggeredBySelector', {
|
||||
animation: false,
|
||||
selector: '.bs-dynamic-tooltip'
|
||||
})
|
||||
|
||||
function duplicateButtons() {
|
||||
const buttonsBlock = document.querySelector('.selectorButtonsBlock')// get first
|
||||
const buttonsBlockClone = buttonsBlock.cloneNode(true)
|
||||
buttonsBlockClone.innerHTML+= new Date().toLocaleString()
|
||||
document.querySelector('#wrapperTriggeredBySelector').append(buttonsBlockClone)
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Generated
+298
-257
File diff suppressed because it is too large
Load Diff
+7
-7
@@ -105,18 +105,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.18.10",
|
||||
"@babel/core": "^7.18.13",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/core": "^7.19.0",
|
||||
"@babel/preset-env": "^7.19.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@rollup/plugin-babel": "^5.3.1",
|
||||
"@rollup/plugin-commonjs": "^22.0.2",
|
||||
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"autoprefixer": "^10.4.10",
|
||||
"bundlewatch": "^0.3.3",
|
||||
"clean-css-cli": "^5.6.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint-config-xo": "^0.42.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-markdown": "^3.0.0",
|
||||
@@ -144,10 +144,10 @@
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-istanbul": "^3.0.0",
|
||||
"rtlcss": "^4.0.0",
|
||||
"sass": "^1.54.8",
|
||||
"sass": "^1.54.9",
|
||||
"shelljs": "^0.8.5",
|
||||
"stylelint": "^14.11.0",
|
||||
"stylelint-config-twbs-bootstrap": "^5.0.0",
|
||||
"stylelint-config-twbs-bootstrap": "^6.0.0",
|
||||
"terser": "^5.15.0",
|
||||
"vnu-jar": "21.10.12"
|
||||
},
|
||||
|
||||
@@ -708,7 +708,7 @@ In the example below, to create an offcanvas navbar that is always collapsed acr
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex" role="search">
|
||||
<form class="d-flex mt-3" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
@@ -768,7 +768,7 @@ When using offcanvas in a dark navbar, be aware that you may need to have a dark
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex" role="search">
|
||||
<form class="d-flex mt-3" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
|
||||
<button class="btn btn-success" type="submit">Search</button>
|
||||
</form>
|
||||
|
||||
@@ -201,7 +201,7 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt
|
||||
| `popperConfig` | null, object, function | `null` | To change Bootstrap's default Popper config, see [Popper's configuration](https://popper.js.org/docs/v2/constructors/#options). When a function is used to create the Popper configuration, it's called with an object that contains the Bootstrap's default Popper configuration. It helps you use and merge the default with your own configuration. The function must return a configuration object for Popper. |
|
||||
| `sanitize` | boolean | `true` | Enable or disable the sanitization. If activated `'template'`, `'content'` and `'title'` options will be sanitized. |
|
||||
| `sanitizeFn` | null, function | `null` | Here you can supply your own sanitize function. This can be useful if you prefer to use a dedicated library to perform sanitization. |
|
||||
| `selector` | string, false | `false` | If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to also apply popovers to dynamically added DOM elements (`jQuery.on` support). See [this issue]({{< param repo >}}/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). |
|
||||
| `selector` | string, false | `false` | If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to also apply popovers to dynamically added DOM elements (`jQuery.on` support). See [this issue]({{< param repo >}}/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). <br>Note: `title` attribute must not be used as a selector. |
|
||||
| `template` | string | `'<div class="popover" role="popover"><div class="popover-arrow"></div><div class="popover-inner"></div></div>'` | Base HTML to use when creating the popover. The popover's `title` will be injected into the `.popover-inner`. `.popover-arrow` will become the popover's arrow. The outermost wrapper element should have the `.popover` class and `role="popover"`. |
|
||||
| `title` | string, element, function | `''` | Default title value if `title` attribute isn't present. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. |
|
||||
| `trigger` | string | `'hover focus'` | How popover is triggered: click, hover, focus, manual. You may pass multiple triggers; separate them with a space. `'manual'` indicates that the popover will be triggered programmatically via the `.popover('show')`, `.popover('hide')` and `.popover('toggle')` methods; this value cannot be combined with any other trigger. `'hover'` on its own will result in popovers that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present. |
|
||||
|
||||
@@ -380,7 +380,7 @@ const scrollSpy = new bootstrap.ScrollSpy(document.body, {
|
||||
| `rootMargin` | string | `0px 0px -25%` | Intersection Observer [rootMargin](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin) valid units, when calculating scroll position. |
|
||||
| `smoothScroll` | boolean | `false` | Enables smooth scrolling when a user clicks on a link that refers to ScrollSpy observables. |
|
||||
| `target` | string, DOM element | `null` | Specifies element to apply Scrollspy plugin. |
|
||||
| `threshold` | array | `[0.1, 0.5, 1]` | `IntersectionObserver` [threshold](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#threshold) valid input, when calculating scroll position.|
|
||||
| `threshold` | array | `[0.1, 0.5, 1]` | `IntersectionObserver` [threshold](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#threshold) valid input, when calculating scroll position. |
|
||||
|
||||
{{< /bs-table >}}
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt
|
||||
| `popperConfig` | null, object, function | `null` | To change Bootstrap's default Popper config, see [Popper's configuration](https://popper.js.org/docs/v2/constructors/#options). When a function is used to create the Popper configuration, it's called with an object that contains the Bootstrap's default Popper configuration. It helps you use and merge the default with your own configuration. The function must return a configuration object for Popper. |
|
||||
| `sanitize` | boolean | `true` | Enable or disable the sanitization. If activated `'template'`, `'content'` and `'title'` options will be sanitized. |
|
||||
| `sanitizeFn` | null, function | `null` | Here you can supply your own sanitize function. This can be useful if you prefer to use a dedicated library to perform sanitization. |
|
||||
| `selector` | string, false | `false` | If a selector is provided, tooltip objects will be delegated to the specified targets. In practice, this is used to also apply tooltips to dynamically added DOM elements (`jQuery.on` support). See [this issue]({{< param repo >}}/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). |
|
||||
| `selector` | string, false | `false` | If a selector is provided, tooltip objects will be delegated to the specified targets. In practice, this is used to also apply tooltips to dynamically added DOM elements (`jQuery.on` support). See [this issue]({{< param repo >}}/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). <br>Note: `title` attribute must not be used as a selector. |
|
||||
| `template` | string | `'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'` | Base HTML to use when creating the tooltip. The tooltip's `title` will be injected into the `.tooltip-inner`. `.tooltip-arrow` will become the tooltip's arrow. The outermost wrapper element should have the `.tooltip` class and `role="tooltip"`. |
|
||||
| `title` | string, element, function | `''` | Default title value if `title` attribute isn't present. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. |
|
||||
| `trigger` | string | `'hover focus'` | How tooltip is triggered: click, hover, focus, manual. You may pass multiple triggers; separate them with a space. `'manual'` indicates that the tooltip will be triggered programmatically via the `.tooltip('show')`, `.tooltip('hide')` and `.tooltip('toggle')` methods; this value cannot be combined with any other trigger. `'hover'` on its own will result in tooltips that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present. |
|
||||
|
||||
@@ -73,7 +73,7 @@ At this point, everything is in the right place, but Vite won't work because we
|
||||
|
||||
With dependencies installed and our project folder ready for us to start coding, we can now configure Vite and run our project locally.
|
||||
|
||||
1. **Open `vite.config.js` in your editor.** Since it's blank, we'll need to add some boilerplate config to it so we can start our server. This part of the config tells Vite were to look for our project's JavaScript and how the development server should behave (pulling from the `src` folder with hot reload).
|
||||
1. **Open `vite.config.js` in your editor.** Since it's blank, we'll need to add some boilerplate config to it so we can start our server. This part of the config tells Vite where to look for our project's JavaScript and how the development server should behave (pulling from the `src` folder with hot reload).
|
||||
|
||||
<!-- eslint-skip -->
|
||||
```js
|
||||
|
||||
@@ -75,7 +75,7 @@ At this point, everything is in the right place, but Webpack won't work because
|
||||
|
||||
With dependencies installed and our project folder ready for us to start coding, we can now configure Webpack and run our project locally.
|
||||
|
||||
1. **Open `webpack.config.js` in your editor.** Since it's blank, we'll need to add some boilerplate config to it so we can start our server. This part of the config tells Webpack were to look for our project's JavaScript, where to output the compiled code to (`dist`), and how the development server should behave (pulling from the `dist` folder with hot reload).
|
||||
1. **Open `webpack.config.js` in your editor.** Since it's blank, we'll need to add some boilerplate config to it so we can start our server. This part of the config tells Webpack where to look for our project's JavaScript, where to output the compiled code to (`dist`), and how the development server should behave (pulling from the `dist` folder with hot reload).
|
||||
|
||||
```js
|
||||
const path = require('path')
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<div class="bd-masthead mb-3" id="content">
|
||||
<div class="container-xxl bd-gutter">
|
||||
<div class="col-md-8 mx-auto text-center">
|
||||
<div class="m-2 p-3 bg-white">
|
||||
<bs-toa class="border"><div class="toast-body bg-info mb-2">dddddd</div></bs-toa>
|
||||
<bs-toa class="border" autohide="false"><div class="toast-body bg-primary">dddddd</div></bs-toa>
|
||||
|
||||
<hr>
|
||||
<button class="btn btn-sm btn-primary" onclick="document.querySelector('bs-toa').show()">show again</button>
|
||||
</div>
|
||||
<a class="d-flex flex-column flex-lg-row justify-content-center align-items-center mb-4 text-dark lh-sm text-decoration-none" href="https://blog.getbootstrap.com/2022/07/19/bootstrap-5-2-0/">
|
||||
<strong class="d-sm-inline-block p-2 me-2 mb-2 mb-lg-0 rounded-3 masthead-notice">New in v5.2</strong>
|
||||
<span class="text-muted">CSS variables, responsive offcanvas, new utilities, and more!</span>
|
||||
|
||||
Reference in New Issue
Block a user