Merge branch 'main' into main-mc-sorry-eric

This commit is contained in:
XhmikosR 2020-12-01 20:43:30 +02:00 committed by GitHub
commit 9e9ff80cb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 557 additions and 361 deletions

View file

@ -128,7 +128,7 @@ Have a bug or a feature request? Please first read the [issue guidelines](https:
Bootstrap's documentation, included in this repo in the root directory, is built with [Hugo](https://gohugo.io/) and publicly hosted on GitHub Pages at <https://v5.getbootstrap.com/>. The docs may also be run locally.
Documentation search is powered by [Algolia's DocSearch](https://community.algolia.com/docsearch/). Working on our search? Be sure to set `debug: true` in `site/assets/js/src/search.js` file.
Documentation search is powered by [Algolia's DocSearch](https://community.algolia.com/docsearch/). Working on our search? Be sure to set `debug: true` in `site/assets/js/search.js`.
### Running documentation locally

View file

@ -15,6 +15,7 @@ import {
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'
/**
* ------------------------------------------------------------------------
@ -23,7 +24,6 @@ import EventHandler from './dom/event-handler'
*/
const NAME = 'alert'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.alert'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -44,19 +44,11 @@ const CLASSNAME_SHOW = 'show'
* ------------------------------------------------------------------------
*/
class Alert {
constructor(element) {
this._element = element
if (this._element) {
Data.setData(element, DATA_KEY, this)
}
}
class Alert extends BaseComponent {
// Getters
static get VERSION() {
return VERSION
static get DATA_KEY() {
return DATA_KEY
}
// Public
@ -72,11 +64,6 @@ class Alert {
this._removeElement(rootElement)
}
dispose() {
Data.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_getRootElement(element) {
@ -134,10 +121,6 @@ class Alert {
alertInstance.close(this)
}
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

44
js/src/base-component.js Normal file
View file

@ -0,0 +1,44 @@
/**
* --------------------------------------------------------------------------
* Bootstrap (v5.0.0-alpha3): base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import Data from './dom/data'
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const VERSION = '5.0.0-alpha3'
class BaseComponent {
constructor(element) {
if (!element) {
return
}
this._element = element
Data.setData(element, this.constructor.DATA_KEY, this)
}
dispose() {
Data.removeData(this._element, this.constructor.DATA_KEY)
this._element = null
}
/** Static */
static getInstance(element) {
return Data.getData(element, this.DATA_KEY)
}
static get VERSION() {
return VERSION
}
}
export default BaseComponent

View file

@ -8,6 +8,7 @@
import { getjQuery, onDOMContentLoaded } from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import BaseComponent from './base-component'
/**
* ------------------------------------------------------------------------
@ -16,7 +17,6 @@ import EventHandler from './dom/event-handler'
*/
const NAME = 'button'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.button'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -33,16 +33,11 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
* ------------------------------------------------------------------------
*/
class Button {
constructor(element) {
this._element = element
Data.setData(element, DATA_KEY, this)
}
class Button extends BaseComponent {
// Getters
static get VERSION() {
return VERSION
static get DATA_KEY() {
return DATA_KEY
}
// Public
@ -52,11 +47,6 @@ class Button {
this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))
}
dispose() {
Data.removeData(this._element, DATA_KEY)
this._element = null
}
// Static
static jQueryInterface(config) {
@ -72,10 +62,6 @@ class Button {
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -21,6 +21,7 @@ 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'
/**
* ------------------------------------------------------------------------
@ -29,7 +30,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'carousel'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -104,8 +104,10 @@ const PointerType = {
* Class Definition
* ------------------------------------------------------------------------
*/
class Carousel {
class Carousel extends BaseComponent {
constructor(element, config) {
super(element)
this._items = null
this._interval = null
this._activeElement = null
@ -116,25 +118,23 @@ class Carousel {
this.touchDeltaX = 0
this._config = this._getConfig(config)
this._element = element
this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)
this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
this._pointerEvent = Boolean(window.PointerEvent)
this._addEventListeners()
Data.setData(element, DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get DATA_KEY() {
return DATA_KEY
}
// Public
next() {
@ -218,12 +218,11 @@ class Carousel {
}
dispose() {
super.dispose()
EventHandler.off(this._element, EVENT_KEY)
Data.removeData(this._element, DATA_KEY)
this._items = null
this._config = null
this._element = null
this._interval = null
this._isPaused = null
this._isSliding = null
@ -590,10 +589,6 @@ class Carousel {
event.preventDefault()
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -21,6 +21,7 @@ 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'
/**
* ------------------------------------------------------------------------
@ -29,7 +30,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'collapse'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.collapse'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -67,10 +67,11 @@ const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]'
* ------------------------------------------------------------------------
*/
class Collapse {
class Collapse extends BaseComponent {
constructor(element, config) {
super(element)
this._isTransitioning = false
this._element = element
this._config = this._getConfig(config)
this._triggerArray = SelectorEngine.find(
`${SELECTOR_DATA_TOGGLE}[href="#${element.id}"],` +
@ -100,20 +101,18 @@ class Collapse {
if (this._config.toggle) {
this.toggle()
}
Data.setData(element, DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get DATA_KEY() {
return DATA_KEY
}
// Public
toggle() {
@ -266,11 +265,9 @@ class Collapse {
}
dispose() {
Data.removeData(this._element, DATA_KEY)
super.dispose()
this._config = null
this._parent = null
this._element = null
this._triggerArray = null
this._isTransitioning = null
}
@ -368,10 +365,6 @@ class Collapse {
Collapse.collapseInterface(this, config)
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -19,6 +19,7 @@ import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import Popper from 'popper.js'
import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
* ------------------------------------------------------------------------
@ -27,7 +28,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'dropdown'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -96,24 +96,20 @@ const DefaultType = {
* ------------------------------------------------------------------------
*/
class Dropdown {
class Dropdown extends BaseComponent {
constructor(element, config) {
this._element = element
super(element)
this._popper = null
this._config = this._getConfig(config)
this._menu = this._getMenuElement()
this._inNavbar = this._detectNavbar()
this._addEventListeners()
Data.setData(element, DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
@ -122,6 +118,10 @@ class Dropdown {
return DefaultType
}
static get DATA_KEY() {
return DATA_KEY
}
// Public
toggle() {
@ -229,9 +229,8 @@ class Dropdown {
}
dispose() {
Data.removeData(this._element, DATA_KEY)
super.dispose()
EventHandler.off(this._element, EVENT_KEY)
this._element = null
this._menu = null
if (this._popper) {
this._popper.destroy()
@ -489,10 +488,6 @@ class Dropdown {
items[index].focus()
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -20,6 +20,7 @@ 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'
/**
* ------------------------------------------------------------------------
@ -28,7 +29,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'modal'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.modal'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -37,15 +37,13 @@ const ESCAPE_KEY = 'Escape'
const Default = {
backdrop: true,
keyboard: true,
focus: true,
show: true
focus: true
}
const DefaultType = {
backdrop: '(boolean|string)',
keyboard: 'boolean',
focus: 'boolean',
show: 'boolean'
focus: 'boolean'
}
const EVENT_HIDE = `hide${EVENT_KEY}`
@ -81,10 +79,11 @@ const SELECTOR_STICKY_CONTENT = '.sticky-top'
* ------------------------------------------------------------------------
*/
class Modal {
class Modal extends BaseComponent {
constructor(element, config) {
super(element)
this._config = this._getConfig(config)
this._element = element
this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, element)
this._backdrop = null
this._isShown = false
@ -92,19 +91,18 @@ class Modal {
this._ignoreBackdropClick = false
this._isTransitioning = false
this._scrollbarWidth = 0
Data.setData(element, DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get DATA_KEY() {
return DATA_KEY
}
// Public
toggle(relatedTarget) {
@ -201,6 +199,8 @@ class Modal {
[window, this._element, this._dialog]
.forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY))
super.dispose()
/**
* `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`
* Do not move `document` in `htmlElements` array
@ -208,10 +208,7 @@ class Modal {
*/
EventHandler.off(document, EVENT_FOCUSIN)
Data.removeData(this._element, DATA_KEY)
this._config = null
this._element = null
this._dialog = null
this._backdrop = null
this._isShown = null
@ -558,15 +555,9 @@ class Modal {
}
data[config](relatedTarget)
} else if (_config.show) {
data.show(relatedTarget)
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -17,7 +17,6 @@ import Tooltip from './tooltip'
*/
const NAME = 'popover'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.popover'
const EVENT_KEY = `.${DATA_KEY}`
const CLASS_PREFIX = 'bs-popover'
@ -67,10 +66,6 @@ const SELECTOR_CONTENT = '.popover-body'
class Popover extends Tooltip {
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
@ -108,7 +103,7 @@ class Popover extends Tooltip {
this.setElementContent(SelectorEngine.findOne(SELECTOR_TITLE, tip), this.getTitle())
let content = this._getContent()
if (typeof content === 'function') {
content = content.call(this.element)
content = content.call(this._element)
}
this.setElementContent(SelectorEngine.findOne(SELECTOR_CONTENT, tip), content)
@ -123,7 +118,7 @@ class Popover extends Tooltip {
}
_getContent() {
return this.element.getAttribute('data-bs-content') ||
return this._element.getAttribute('data-bs-content') ||
this.config.content
}
@ -161,10 +156,6 @@ class Popover extends Tooltip {
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -17,6 +17,7 @@ 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'
/**
* ------------------------------------------------------------------------
@ -25,7 +26,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'scrollspy'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.scrollspy'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -66,9 +66,9 @@ const METHOD_POSITION = 'position'
* ------------------------------------------------------------------------
*/
class ScrollSpy {
class ScrollSpy extends BaseComponent {
constructor(element, config) {
this._element = element
super(element)
this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = this._getConfig(config)
this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS}, ${this._config.target} ${SELECTOR_LIST_ITEMS}, ${this._config.target} .${CLASS_NAME_DROPDOWN_ITEM}`
@ -81,20 +81,18 @@ class ScrollSpy {
this.refresh()
this._process()
Data.setData(element, DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
static get DATA_KEY() {
return DATA_KEY
}
// Public
refresh() {
@ -141,10 +139,9 @@ class ScrollSpy {
}
dispose() {
Data.removeData(this._element, DATA_KEY)
super.dispose()
EventHandler.off(this._scrollElement, EVENT_KEY)
this._element = null
this._scrollElement = null
this._config = null
this._selector = null
@ -301,10 +298,6 @@ class ScrollSpy {
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -17,6 +17,7 @@ import {
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
* ------------------------------------------------------------------------
@ -25,7 +26,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'tab'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
@ -56,17 +56,11 @@ const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'
* ------------------------------------------------------------------------
*/
class Tab {
constructor(element) {
this._element = element
Data.setData(this._element, DATA_KEY, this)
}
class Tab extends BaseComponent {
// Getters
static get VERSION() {
return VERSION
static get DATA_KEY() {
return DATA_KEY
}
// Public
@ -127,11 +121,6 @@ class Tab {
}
}
dispose() {
Data.removeData(this._element, DATA_KEY)
this._element = null
}
// Private
_activate(element, container, callback) {
@ -217,10 +206,6 @@ class Tab {
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -17,6 +17,7 @@ import {
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import BaseComponent from './base-component'
/**
* ------------------------------------------------------------------------
@ -25,7 +26,6 @@ import Manipulator from './dom/manipulator'
*/
const NAME = 'toast'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.toast'
const EVENT_KEY = `.${DATA_KEY}`
@ -60,21 +60,17 @@ const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="toast"]'
* ------------------------------------------------------------------------
*/
class Toast {
class Toast extends BaseComponent {
constructor(element, config) {
this._element = element
super(element)
this._config = this._getConfig(config)
this._timeout = null
this._setListeners()
Data.setData(element, DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get DefaultType() {
return DefaultType
}
@ -83,6 +79,10 @@ class Toast {
return Default
}
static get DATA_KEY() {
return DATA_KEY
}
// Public
show() {
@ -159,9 +159,8 @@ class Toast {
}
EventHandler.off(this._element, EVENT_CLICK_DISMISS)
Data.removeData(this._element, DATA_KEY)
this._element = null
super.dispose()
this._config = null
}
@ -208,10 +207,6 @@ class Toast {
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -26,6 +26,7 @@ import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import Popper from 'popper.js'
import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
* ------------------------------------------------------------------------
@ -34,7 +35,6 @@ import SelectorEngine from './dom/selector-engine'
*/
const NAME = 'tooltip'
const VERSION = '5.0.0-alpha3'
const DATA_KEY = 'bs.tooltip'
const EVENT_KEY = `.${DATA_KEY}`
const CLASS_PREFIX = 'bs-tooltip'
@ -124,12 +124,14 @@ const TRIGGER_MANUAL = 'manual'
* ------------------------------------------------------------------------
*/
class Tooltip {
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)
// private
this._isEnabled = true
this._timeout = 0
@ -138,20 +140,14 @@ class Tooltip {
this._popper = null
// Protected
this.element = element
this.config = this._getConfig(config)
this.tip = null
this._setListeners()
Data.setData(element, this.constructor.DATA_KEY, this)
}
// Getters
static get VERSION() {
return VERSION
}
static get Default() {
return Default
}
@ -227,10 +223,8 @@ class Tooltip {
dispose() {
clearTimeout(this._timeout)
Data.removeData(this.element, this.constructor.DATA_KEY)
EventHandler.off(this.element, this.constructor.EVENT_KEY)
EventHandler.off(this.element.closest(`.${CLASS_NAME_MODAL}`), 'hide.bs.modal', this._hideModalHandler)
EventHandler.off(this._element, this.constructor.EVENT_KEY)
EventHandler.off(this._element.closest(`.${CLASS_NAME_MODAL}`), 'hide.bs.modal', this._hideModalHandler)
if (this.tip) {
this.tip.parentNode.removeChild(this.tip)
@ -245,22 +239,22 @@ class Tooltip {
}
this._popper = null
this.element = null
this.config = null
this.tip = null
super.dispose()
}
show() {
if (this.element.style.display === 'none') {
if (this._element.style.display === 'none') {
throw new Error('Please use show on visible elements')
}
if (this.isWithContent() && this._isEnabled) {
const showEvent = EventHandler.trigger(this.element, this.constructor.Event.SHOW)
const shadowRoot = findShadowRoot(this.element)
const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)
const shadowRoot = findShadowRoot(this._element)
const isInTheDom = shadowRoot === null ?
this.element.ownerDocument.documentElement.contains(this.element) :
shadowRoot.contains(this.element)
this._element.ownerDocument.documentElement.contains(this._element) :
shadowRoot.contains(this._element)
if (showEvent.defaultPrevented || !isInTheDom) {
return
@ -270,7 +264,7 @@ class Tooltip {
const tipId = getUID(this.constructor.NAME)
tip.setAttribute('id', tipId)
this.element.setAttribute('aria-describedby', tipId)
this._element.setAttribute('aria-describedby', tipId)
this.setContent()
@ -279,7 +273,7 @@ class Tooltip {
}
const placement = typeof this.config.placement === 'function' ?
this.config.placement.call(this, tip, this.element) :
this.config.placement.call(this, tip, this._element) :
this.config.placement
const attachment = this._getAttachment(placement)
@ -288,13 +282,13 @@ class Tooltip {
const container = this._getContainer()
Data.setData(tip, this.constructor.DATA_KEY, this)
if (!this.element.ownerDocument.documentElement.contains(this.tip)) {
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
container.appendChild(tip)
}
EventHandler.trigger(this.element, this.constructor.Event.INSERTED)
EventHandler.trigger(this._element, this.constructor.Event.INSERTED)
this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment))
this._popper = new Popper(this._element, tip, this._getPopperConfig(attachment))
tip.classList.add(CLASS_NAME_SHOW)
@ -321,7 +315,7 @@ class Tooltip {
const prevHoverState = this._hoverState
this._hoverState = null
EventHandler.trigger(this.element, this.constructor.Event.SHOWN)
EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
if (prevHoverState === HOVER_STATE_OUT) {
this._leave(null, this)
@ -350,12 +344,12 @@ class Tooltip {
}
this._cleanTipClass()
this.element.removeAttribute('aria-describedby')
EventHandler.trigger(this.element, this.constructor.Event.HIDDEN)
this._element.removeAttribute('aria-describedby')
EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
this._popper.destroy()
}
const hideEvent = EventHandler.trigger(this.element, this.constructor.Event.HIDE)
const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)
if (hideEvent.defaultPrevented) {
return
}
@ -450,11 +444,11 @@ class Tooltip {
}
getTitle() {
let title = this.element.getAttribute('data-bs-original-title')
let title = this._element.getAttribute('data-bs-original-title')
if (!title) {
title = typeof this.config.title === 'function' ?
this.config.title.call(this.element) :
this.config.title.call(this._element) :
this.config.title
}
@ -503,7 +497,7 @@ class Tooltip {
offset.fn = data => {
data.offsets = {
...data.offsets,
...(this.config.offset(data.offsets, this.element) || {})
...(this.config.offset(data.offsets, this._element) || {})
}
return data
@ -536,7 +530,7 @@ class Tooltip {
triggers.forEach(trigger => {
if (trigger === 'click') {
EventHandler.on(this.element,
EventHandler.on(this._element,
this.constructor.Event.CLICK,
this.config.selector,
event => this.toggle(event)
@ -549,12 +543,12 @@ class Tooltip {
this.constructor.Event.MOUSELEAVE :
this.constructor.Event.FOCUSOUT
EventHandler.on(this.element,
EventHandler.on(this._element,
eventIn,
this.config.selector,
event => this._enter(event)
)
EventHandler.on(this.element,
EventHandler.on(this._element,
eventOut,
this.config.selector,
event => this._leave(event)
@ -563,12 +557,12 @@ class Tooltip {
})
this._hideModalHandler = () => {
if (this.element) {
if (this._element) {
this.hide()
}
}
EventHandler.on(this.element.closest(`.${CLASS_NAME_MODAL}`),
EventHandler.on(this._element.closest(`.${CLASS_NAME_MODAL}`),
'hide.bs.modal',
this._hideModalHandler
)
@ -585,12 +579,16 @@ class Tooltip {
}
_fixTitle() {
const title = this.element.getAttribute('title')
const originalTitleType = typeof this.element.getAttribute('data-bs-original-title')
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 || '')
this.element.setAttribute('title', '')
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', '')
}
}
@ -683,7 +681,7 @@ class Tooltip {
}
_getConfig(config) {
const dataAttributes = Manipulator.getDataAttributes(this.element)
const dataAttributes = Manipulator.getDataAttributes(this._element)
Object.keys(dataAttributes).forEach(dataAttr => {
if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
@ -792,10 +790,6 @@ class Tooltip {
}
})
}
static getInstance(element) {
return Data.getData(element, DATA_KEY)
}
}
/**

View file

@ -170,4 +170,24 @@ describe('Alert', () => {
expect(fixtureEl.querySelector('.alert')).not.toBeNull()
})
})
describe('getInstance', () => {
it('should return alert instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const alert = new Alert(div)
expect(Alert.getInstance(div)).toEqual(alert)
expect(Alert.getInstance(div)).toBeInstanceOf(Alert)
})
it('should return null when there is no alert instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Alert.getInstance(div)).toEqual(null)
})
})
})

View file

@ -128,4 +128,24 @@ describe('Button', () => {
expect(btnEl.classList.contains('active')).toEqual(false)
})
})
describe('getInstance', () => {
it('should return button instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const button = new Button(div)
expect(Button.getInstance(div)).toEqual(button)
expect(Button.getInstance(div)).toBeInstanceOf(Button)
})
it('should return null when there is no button instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Button.getInstance(div)).toEqual(null)
})
})
})

View file

@ -1062,6 +1062,26 @@ describe('Carousel', () => {
})
})
describe('getInstance', () => {
it('should return carousel instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const carousel = new Carousel(div)
expect(Carousel.getInstance(div)).toEqual(carousel)
expect(Carousel.getInstance(div)).toBeInstanceOf(Carousel)
})
it('should return null when there is no carousel instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Carousel.getInstance(div)).toEqual(null)
})
})
describe('jQueryInterface', () => {
it('should create a carousel', () => {
fixtureEl.innerHTML = '<div></div>'

View file

@ -812,6 +812,7 @@ describe('Collapse', () => {
const collapse = new Collapse(div)
expect(Collapse.getInstance(div)).toEqual(collapse)
expect(Collapse.getInstance(div)).toBeInstanceOf(Collapse)
})
it('should return null when there is no collapse instance', () => {

View file

@ -1612,6 +1612,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(div)
expect(Dropdown.getInstance(div)).toEqual(dropdown)
expect(Dropdown.getInstance(div)).toBeInstanceOf(Dropdown)
})
it('should return null when there is no dropdown instance', () => {

View file

@ -1108,6 +1108,7 @@ describe('Modal', () => {
const modal = new Modal(div)
expect(Modal.getInstance(div)).toEqual(modal)
expect(Modal.getInstance(div)).toBeInstanceOf(Modal)
})
it('should return null when there is no modal instance', () => {

View file

@ -253,6 +253,7 @@ describe('Popover', () => {
const popover = new Popover(popoverEl)
expect(Popover.getInstance(popoverEl)).toEqual(popover)
expect(Popover.getInstance(popoverEl)).toBeInstanceOf(Popover)
})
it('should return null when there is no popover instance', () => {

View file

@ -634,6 +634,16 @@ describe('ScrollSpy', () => {
})
describe('getInstance', () => {
it('should return scrollspy instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const scrollSpy = new ScrollSpy(div)
expect(ScrollSpy.getInstance(div)).toEqual(scrollSpy)
expect(ScrollSpy.getInstance(div)).toBeInstanceOf(ScrollSpy)
})
it('should return null if there is no instance', () => {
expect(ScrollSpy.getInstance(fixtureEl)).toEqual(null)
})

View file

@ -417,6 +417,7 @@ describe('Tab', () => {
const tab = new Tab(divEl)
expect(Tab.getInstance(divEl)).toEqual(tab)
expect(Tab.getInstance(divEl)).toBeInstanceOf(Tab)
})
})

View file

@ -384,6 +384,7 @@ describe('Toast', () => {
const toast = new Toast(div)
expect(Toast.getInstance(div)).toEqual(toast)
expect(Toast.getInstance(div)).toBeInstanceOf(Toast)
})
it('should return null when there is no toast instance', () => {

View file

@ -1030,6 +1030,79 @@ describe('Tooltip', () => {
})
})
describe('getInstance', () => {
it('should return tooltip instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const alert = new Tooltip(div)
expect(Tooltip.getInstance(div)).toEqual(alert)
expect(Tooltip.getInstance(div)).toBeInstanceOf(Tooltip)
})
it('should return null when there is no tooltip instance', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Tooltip.getInstance(div)).toEqual(null)
})
})
describe('aria-label', () => {
it('should add the aria-label attribute for referencing original title', done => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
expect(tooltipShown).toBeDefined()
expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
done()
})
tooltip.show()
})
it('should not add the aria-label attribute if the attribute already exists', done => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" aria-label="Different label" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
expect(tooltipShown).toBeDefined()
expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
done()
})
tooltip.show()
})
it('should not add the aria-label attribute if the element has text content', done => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">text content</a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
expect(tooltipShown).toBeDefined()
expect(tooltipEl.getAttribute('aria-label')).toBeNull()
done()
})
tooltip.show()
})
})
describe('jQueryInterface', () => {
it('should create a tooltip', () => {
fixtureEl.innerHTML = '<div></div>'

322
package-lock.json generated
View file

@ -38,9 +38,9 @@
"dev": true
},
"@babel/core": {
"version": "7.12.8",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.8.tgz",
"integrity": "sha512-ra28JXL+5z73r1IC/t+FT1ApXU5LsulFDnTDntNfLQaScJUJmcHL5Qxm/IWanCToQk3bPWQo5bflbplU5r15pg==",
"version": "7.12.9",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.9.tgz",
"integrity": "sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
@ -49,7 +49,7 @@
"@babel/helpers": "^7.12.5",
"@babel/parser": "^7.12.7",
"@babel/template": "^7.12.7",
"@babel/traverse": "^7.12.8",
"@babel/traverse": "^7.12.9",
"@babel/types": "^7.12.7",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
@ -1006,9 +1006,9 @@
}
},
"@babel/traverse": {
"version": "7.12.8",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.8.tgz",
"integrity": "sha512-EIRQXPTwFEGRZyu6gXbjfpNORN1oZvwuzJbxcXjAgWV0iqXYDszN1Hx3FVm6YgZfu1ZQbCVAk3l+nIw95Xll9Q==",
"version": "7.12.9",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.9.tgz",
"integrity": "sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
@ -1141,9 +1141,9 @@
}
},
"@rollup/plugin-babel": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.1.tgz",
"integrity": "sha512-Jd7oqFR2dzZJ3NWANDyBjwTtX/lYbZpVcmkHrfQcpvawHs9E4c0nYk5U2mfZ6I/DZcIvy506KZJi54XK/jxH7A==",
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.2.tgz",
"integrity": "sha512-MjmH7GvFT4TW8xFdIeFS3wqIX646y5tACdxkTO+khbHvS3ZcVJL6vkAHLw2wqPmkhwCfWHoNsp15VYNwW6JEJA==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.10.4",
@ -1151,9 +1151,9 @@
}
},
"@rollup/plugin-commonjs": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz",
"integrity": "sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==",
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.0.0.tgz",
"integrity": "sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.1.0",
@ -1174,9 +1174,9 @@
}
},
"@rollup/plugin-node-resolve": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz",
"integrity": "sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A==",
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.0.0.tgz",
"integrity": "sha512-8Hrmwjn1pLYjUxcv7U7IPP0qfnzEJWHyHE6CaZ8jbLM+8axaarJRB1jB6JgKTDp5gNga+TpsgX6F8iuvgOerKQ==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.1.0",
@ -1184,7 +1184,7 @@
"builtin-modules": "^3.1.0",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.17.0"
"resolve": "^1.19.0"
}
},
"@rollup/pluginutils": {
@ -1260,9 +1260,9 @@
"dev": true
},
"@types/node": {
"version": "14.14.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz",
"integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw==",
"version": "14.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
"integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==",
"dev": true
},
"@types/normalize-package-data": {
@ -1468,14 +1468,22 @@
"dev": true,
"optional": true
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
"array-includes": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
"integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz",
"integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0",
"es-abstract": "^1.18.0-next.1",
"get-intrinsic": "^1.0.1",
"is-string": "^1.0.5"
}
},
@ -1501,28 +1509,6 @@
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
},
"arraybuffer.slice": {
@ -1577,13 +1563,13 @@
"optional": true
},
"autoprefixer": {
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.0.2.tgz",
"integrity": "sha512-okBmu9OMdt6DNEcZmnl0IYVv8Xl/xYWRSnc2OJ9UJEOt1u30opG1B8aLsViqKryBaYv1SKB4f85fOGZs5zYxHQ==",
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.0.4.tgz",
"integrity": "sha512-hmjYejN/WTyPP9cdNmiwtwqM8/ACVJPD5ExtwoOceQohNbgnFNiwpL2+U4bXS8aXozBL00WvH6WhqbuHf0Fgfg==",
"dev": true,
"requires": {
"browserslist": "^4.14.7",
"caniuse-lite": "^1.0.30001157",
"caniuse-lite": "^1.0.30001161",
"colorette": "^1.2.1",
"normalize-range": "^0.1.2",
"num2fraction": "^1.2.2",
@ -2479,9 +2465,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001159",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001159.tgz",
"integrity": "sha512-w9Ph56jOsS8RL20K9cLND3u/+5WASWdhC/PPrf+V3/HsM3uHOavWOR1Xzakbv4Puo/srmPHudkmCRWM7Aq+/UA==",
"version": "1.0.30001164",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001164.tgz",
"integrity": "sha512-G+A/tkf4bu0dSp9+duNiXc7bGds35DioCyC6vgK2m/rjA4Krpy5WeZgZyfH2f0wj2kI6yAWWucyap6oOwmY1mg==",
"dev": true
},
"caw": {
@ -2974,6 +2960,12 @@
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
"dev": true
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
"dev": true
},
"copy-descriptor": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
@ -2982,18 +2974,18 @@
"optional": true
},
"core-js": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
"integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==",
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"dev": true
},
"core-js-compat": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.7.0.tgz",
"integrity": "sha512-V8yBI3+ZLDVomoWICO6kq/CD28Y4r1M7CWeO4AGpMdMfseu8bkSubBmUPySMGKRTS+su4XQ07zUkAsiu9FCWTg==",
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.0.tgz",
"integrity": "sha512-o9QKelQSxQMYWHXc/Gc4L8bx/4F7TTraE5rhuN8I7mKBt5dBIUpXpIR3omv70ebr8ST5R3PqbDQr+ZI3+Tt1FQ==",
"dev": true,
"requires": {
"browserslist": "^4.14.6",
"browserslist": "^4.14.7",
"semver": "7.0.0"
},
"dependencies": {
@ -3551,9 +3543,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.605",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.605.tgz",
"integrity": "sha512-PDZlEh6qelLaFLXYuIvB0aeqNoq7JS7i7aSHFds/ZnHosxyrTZftD8o84K9OwZWR+tnQFwoL76Yd5ROAhIWojQ==",
"version": "1.3.612",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.612.tgz",
"integrity": "sha512-CdrdX1B6mQqxfw+51MPWB5qA6TKWjza9f5voBtUlRfEZEwZiFaxJLrhFI8zHE9SBAuGt4h84rQU6Ho9Bauo1LA==",
"dev": true
},
"emoji-regex": {
@ -3703,9 +3695,9 @@
}
},
"es-abstract": {
"version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
@ -3713,6 +3705,7 @@
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
@ -4350,6 +4343,52 @@
}
}
},
"express": {
"version": "4.17.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
"dev": true,
"requires": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"dependencies": {
"cookie": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
"dev": true
}
}
},
"ext-list": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz",
@ -4801,6 +4840,12 @@
"dev": true,
"optional": true
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"dev": true
},
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@ -5408,9 +5453,9 @@
}
},
"hugo-bin": {
"version": "0.66.2",
"resolved": "https://registry.npmjs.org/hugo-bin/-/hugo-bin-0.66.2.tgz",
"integrity": "sha512-T/U35qAnzKubHzMi10uolB50e+RgHigHyAEnhGy6upCEp2H1sbDtsmqyzIhc+gMK8VIzJoU3vg/nqfpkFKOwsA==",
"version": "0.67.0",
"resolved": "https://registry.npmjs.org/hugo-bin/-/hugo-bin-0.67.0.tgz",
"integrity": "sha512-EvNzhWGqE2Pb1idPPLlyIIK2VC3so3sdYShC2ohxsHCGNVWoe/D25KPJ5oIVpakxTu4SSH7Kr37RUMsa0yGQ+g==",
"dev": true,
"requires": {
"bin-wrapper": "^4.1.0",
@ -5582,6 +5627,12 @@
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
"dev": true
},
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"dev": true
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@ -5659,9 +5710,9 @@
}
},
"is-core-module": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz",
"integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
"integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
@ -6423,19 +6474,19 @@
"dev": true
},
"linkinator": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/linkinator/-/linkinator-2.2.2.tgz",
"integrity": "sha512-mcMMbo8n8p6qDuCQ5zZaBbxKK9jq79oWsoduBIGc0rRzukDmfLr7t6Ok1jJA7aHHFX251NmtbX2LdQvD54UHow==",
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/linkinator/-/linkinator-2.4.0.tgz",
"integrity": "sha512-YSecbj3/LF2xAnC4jDHEunu5Xa88lWgMemcOX4VFjtnKmYyQqhFOP5rNzqVodFUBiEgDKfNWCB9AxqfL3qMbbQ==",
"dev": true,
"requires": {
"chalk": "^4.0.0",
"cheerio": "^1.0.0-rc.2",
"finalhandler": "^1.1.2",
"express": "^4.17.1",
"gaxios": "^4.0.0",
"jsonexport": "^3.0.0",
"marked": "^1.2.5",
"meow": "^8.0.0",
"p-queue": "^6.2.1",
"serve-static": "^1.14.1",
"server-destroy": "^1.0.1",
"update-notifier": "^5.0.0"
},
@ -6803,6 +6854,12 @@
"object-visit": "^1.0.0"
}
},
"marked": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/marked/-/marked-1.2.5.tgz",
"integrity": "sha512-2AlqgYnVPOc9WDyWu7S5DJaEZsfk6dNh/neatQ3IHUW4QLutM/VPSH9lG7bif+XjFWc9K9XR3QvR+fXuECmfdA==",
"dev": true
},
"mathml-tag-names": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
@ -6936,12 +6993,24 @@
}
}
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
"dev": true
},
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
"dev": true
},
"micromark": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/micromark/-/micromark-2.10.1.tgz",
@ -7115,9 +7184,9 @@
"dev": true
},
"nanoid": {
"version": "3.1.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.18.tgz",
"integrity": "sha512-rndlDjbbHbcV3xi+R2fpJ+PbGMdfBxz5v1fATIQFq0DP64FsicQdwnKLy47K4kZHdRpmQXtz24eGsxQqamzYTA==",
"version": "3.1.19",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.19.tgz",
"integrity": "sha512-giCapaolEGFzITf00P2rSIjWoY3aWS3bb7AzjnfFJH4EOaAPLtyUrr3C5nQ//x4v1wTcU1JgtBpOKPOqqHrakg==",
"dev": true
},
"nanomatch": {
@ -7563,9 +7632,9 @@
"dev": true
},
"object-inspect": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz",
"integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==",
"dev": true
},
"object-keys": {
@ -7607,14 +7676,14 @@
}
},
"object.values": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
"integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz",
"integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1",
"function-bind": "^1.1.1",
"es-abstract": "^1.18.0-next.1",
"has": "^1.0.3"
}
},
@ -8026,6 +8095,12 @@
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
"dev": true
},
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
@ -8525,9 +8600,9 @@
"dev": true
},
"postcss-reporter": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.1.tgz",
"integrity": "sha512-R9AK80KIqqMb+lwGRBcRkXS7r96VCTxrZvvrfibyA/dWjqctwx7leHMCC05A9HbW8PnChwOWwrmISwp5HQu5wg==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.2.tgz",
"integrity": "sha512-JyQ96NTQQsso42y6L1H1RqHfWH1C3Jr0pt91mVv5IdYddZAE9DUZxuferNgk6q0o6vBVOrfVJb10X1FgDzjmDw==",
"dev": true,
"requires": {
"colorette": "^1.2.1",
@ -8535,8 +8610,7 @@
"lodash.forown": "^4.4.0",
"lodash.get": "^4.4.2",
"lodash.groupby": "^4.6.0",
"lodash.sortby": "^4.7.0",
"log-symbols": "^4.0.0"
"lodash.sortby": "^4.7.0"
}
},
"postcss-resolve-nested-selector": {
@ -8727,6 +8801,16 @@
"integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=",
"dev": true
},
"proxy-addr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
"dev": true,
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.9.1"
}
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@ -9198,9 +9282,9 @@
}
},
"rollup": {
"version": "2.33.3",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.33.3.tgz",
"integrity": "sha512-RpayhPTe4Gu/uFGCmk7Gp5Z9Qic2VsqZ040G+KZZvsZYdcuWaJg678JeDJJvJeEQXminu24a2au+y92CUWVd+w==",
"version": "2.34.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.34.0.tgz",
"integrity": "sha512-dW5iLvttZzdVehjEuNJ1bWvuMEJjOWGmnuFS82WeKHTGXDkRHQeq/ExdifkSyJv9dLcR86ysKRmrIDyR6O0X8g==",
"dev": true,
"requires": {
"fsevents": "~2.1.2"
@ -9957,9 +10041,9 @@
}
},
"spdx-license-ids": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
"integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
"integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
"dev": true
},
"specificity": {
@ -10090,28 +10174,6 @@
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
},
"string.prototype.trimend": {
@ -11123,9 +11185,9 @@
}
},
"unist-util-is": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.3.tgz",
"integrity": "sha512-bTofCFVx0iQM8Jqb1TBDVRIQW03YkD3p66JOd/aCWuqzlLyUtx1ZAGw/u+Zw+SttKvSVcvTiKYbfrtLoLefykw==",
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.4.tgz",
"integrity": "sha512-3dF39j/u423v4BBQrk1AQ2Ve1FxY5W3JKwXxVFzBODQ6WEvccguhgp802qQLKSnxPODE6WuRZtV+ohlUg4meBA==",
"dev": true
},
"unist-util-stringify-position": {
@ -11368,6 +11430,12 @@
"spdx-expression-parse": "^3.0.0"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
"dev": true
},
"vfile": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.0.tgz",
@ -11593,9 +11661,9 @@
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true
},
"yallist": {

View file

@ -94,12 +94,12 @@
},
"devDependencies": {
"@babel/cli": "^7.12.8",
"@babel/core": "^7.12.8",
"@babel/core": "^7.12.9",
"@babel/preset-env": "^7.12.7",
"@rollup/plugin-babel": "^5.2.1",
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-node-resolve": "^10.0.0",
"autoprefixer": "^10.0.2",
"@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-commonjs": "^17.0.0",
"@rollup/plugin-node-resolve": "^11.0.0",
"autoprefixer": "^10.0.4",
"bundlewatch": "^0.3.1",
"clean-css-cli": "^4.3.0",
"cross-env": "^7.0.2",
@ -110,7 +110,7 @@
"find-unused-sass-variables": "^3.0.0",
"glob": "^7.1.6",
"hammer-simulator": "0.0.1",
"hugo-bin": "^0.66.2",
"hugo-bin": "^0.67.0",
"ip": "^1.1.5",
"jquery": "^3.5.1",
"karma": "^5.2.3",
@ -122,14 +122,14 @@
"karma-jasmine": "^4.0.1",
"karma-jasmine-html-reporter": "^1.5.4",
"karma-rollup-preprocessor": "^7.0.5",
"linkinator": "^2.2.2",
"linkinator": "^2.4.0",
"lockfile-lint": "^4.3.7",
"nodemon": "^2.0.6",
"npm-run-all": "^4.1.5",
"popper.js": "^1.16.1",
"postcss": "^8.1.10",
"postcss-cli": "^8.3.0",
"rollup": "^2.33.3",
"rollup": "^2.34.0",
"rollup-plugin-istanbul": "^2.0.1",
"sass": "^1.29.0",
"shelljs": "^0.8.4",

View file

@ -18,7 +18,7 @@
float: left; // Suppress inline spacings and underlining of the separator
padding-right: $breadcrumb-item-padding-x;
color: $breadcrumb-divider-color;
content: escape-svg($breadcrumb-divider);
content: var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider));
}
}

View file

@ -132,7 +132,7 @@
@include border-bottom-radius(0);
}
> .btn:not(:first-child),
> .btn ~ .btn,
> .btn-group:not(:first-child) > .btn {
@include border-top-radius(0);
}

View file

@ -4,7 +4,6 @@
.table {
--#{$variable-prefix}table-bg: #{$table-bg};
--#{$variable-prefix}table-accent-bg: transparent;
--#{$variable-prefix}table-striped-color: #{$table-striped-color};
--#{$variable-prefix}table-striped-bg: #{$table-striped-bg};
--#{$variable-prefix}table-active-color: #{$table-active-color};

View file

@ -1245,15 +1245,15 @@ $figure-caption-color: $gray-600 !default;
// Breadcrumbs
$breadcrumb-font-size: null !default;
$breadcrumb-padding-y: $spacer / 2 !default;
$breadcrumb-padding-x: $spacer !default;
$breadcrumb-padding-y: 0 !default;
$breadcrumb-padding-x: 0 !default;
$breadcrumb-item-padding-x: .5rem !default;
$breadcrumb-margin-bottom: 1rem !default;
$breadcrumb-bg: $gray-200 !default;
$breadcrumb-bg: null !default;
$breadcrumb-divider-color: $gray-600 !default;
$breadcrumb-active-color: $gray-600 !default;
$breadcrumb-divider: quote("/") !default;
$breadcrumb-border-radius: $border-radius !default;
$breadcrumb-border-radius: null !default;
// Carousel

View file

@ -3,10 +3,13 @@ layout: docs
title: Breadcrumb
description: Indicate the current page's location within a navigational hierarchy that automatically adds separators via CSS.
group: components
toc: true
---
## Example
Use an ordered or unordered list with linked list items to create a minimally styled breadcrumb. Use our utilities to add additional styles as desired.
{{< example >}}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
@ -30,21 +33,51 @@ group: components
</nav>
{{< /example >}}
## Changing the separator
## Dividers
Separators are automatically added in CSS through [`::before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) and [`content`](https://developer.mozilla.org/en-US/docs/Web/CSS/content). They can be changed by changing `$breadcrumb-divider`. The [quote](https://sass-lang.com/documentation/modules/string#quote) function is needed to generate the quotes around a string, so if you want `>` as separator, you can use this:
Dividers are automatically added in CSS through [`::before`](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) and [`content`](https://developer.mozilla.org/en-US/docs/Web/CSS/content). They can be changed by modifying a local CSS custom property `--bs-breadcrumb-divider`, or through the `$breadcrumb-divider` Sass variable. We default to our Sass variable, which is set as a fallback to the custom property. This way, you get a global divider that you can override without recompiling CSS at any time.
{{< example >}}
<nav style="--bs-breadcrumb-divider: '>';" aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Library</li>
</ol>
</nav>
{{< /example >}}
When modifying via Sass, the [quote](https://sass-lang.com/documentation/modules/string#quote) function is required to generate the quotes around a string. For example, using `>` as the divider, you can use this:
```scss
$breadcrumb-divider: quote(">");
```
It's also possible to use an **embedded SVG icon**:
It's also possible to use an **embedded SVG icon**. Apply it via our CSS custom property, or use the Sass variable.
{{< example >}}
<nav style="--bs-breadcrumb-divider: url(&#34;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='currentColor'/%3E%3C/svg%3E&#34;);" aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Library</li>
</ol>
</nav>
{{< /example >}}
```scss
$breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='currentColor'/%3E%3C/svg%3E");
```
The separator can be removed by setting `$breadcrumb-divider` to `none`:
You can also remove the divider setting `--bs-breadcrumb-divider: '';` (empty strings in CSS custom properties counts as a value), or setting the Sass variable to `$breadcrumb-divider: none;`.
{{< example >}}
<nav style="--bs-breadcrumb-divider: '';" aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Library</li>
</ol>
</nav>
{{< /example >}}
```scss
$breadcrumb-divider: none;

View file

@ -877,12 +877,6 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
<td><code>true</code></td>
<td>Puts the focus on the modal when initialized.</td>
</tr>
<tr>
<td><code>show</code></td>
<td>boolean</td>
<td><code>true</code></td>
<td>Shows the modal when initialized.</td>
</tr>
</tbody>
</table>

View file

@ -13,7 +13,7 @@ Bootstrap sets basic global display, typography, and link styles. When more cont
- Use a [native font stack]({{< docsref "/content/reboot#native-font-stack" >}}) that selects the best `font-family` for each OS and device.
- For a more inclusive and accessible type scale, we use the browser's default root `font-size` (typically 16px) so visitors can customize their browser defaults as needed.
- Use the `$font-family-base`, `$font-size-base`, and `$line-height-base` attributes as our typographic base applied to the `<body>`.
- Set the global link color via `$link-color` and apply link underlines only on `:hover`.
- Set the global link color via `$link-color`.
- Use `$body-bg` to set a `background-color` on the `<body>` (`#fff` by default).
These styles can be found within `_reboot.scss`, and the global variables are defined in `_variables.scss`. Make sure to set `$font-size-base` in `rem`.

View file

@ -17,6 +17,13 @@ toc: true
- Renamed `scale-color()` function to `shift-color()` to avoid collision with Sass's own color scaling function.
### Components
#### Breadcrumbs
- Simplified the default appearance of breadcrumbs by removing `padding`, `background-color`, and `border-radius`.
- Added new CSS custom property `--bs-breadcrumb-divider` for easy customization without needing to recompile CSS.
## v5.0.0-alpha3
### Browser support
@ -267,6 +274,7 @@ Changes to Reboot, typography, tables, and more.
- Removed individual `$display-*-weight` variables for a single `$display-font-weight`.
- Added two new `.display-*` heading styles, `.display-5` and `.display-6`.
- Resized existing display headings for a slightly more consistent set of `font-size`s.
- Links are underlined by default (not just on hover), unless they're part of specific components.
### Forms