mirror of
https://github.com/twbs/bootstrap.git
synced 2025-09-28 06:20:15 +02:00
Merge branch 'v4-dev' into dropdown-keyboard
This commit is contained in:
@@ -120,10 +120,9 @@ const Carousel = (($) => {
|
||||
// public
|
||||
|
||||
next() {
|
||||
if (this._isSliding) {
|
||||
throw new Error('Carousel is sliding')
|
||||
if (!this._isSliding) {
|
||||
this._slide(Direction.NEXT)
|
||||
}
|
||||
this._slide(Direction.NEXT)
|
||||
}
|
||||
|
||||
nextWhenVisible() {
|
||||
@@ -134,10 +133,9 @@ const Carousel = (($) => {
|
||||
}
|
||||
|
||||
prev() {
|
||||
if (this._isSliding) {
|
||||
throw new Error('Carousel is sliding')
|
||||
if (!this._isSliding) {
|
||||
this._slide(Direction.PREV)
|
||||
}
|
||||
this._slide(Direction.PREV)
|
||||
}
|
||||
|
||||
pause(event) {
|
||||
@@ -282,9 +280,13 @@ const Carousel = (($) => {
|
||||
|
||||
|
||||
_triggerSlideEvent(relatedTarget, eventDirectionName) {
|
||||
const targetIndex = this._getItemIndex(relatedTarget)
|
||||
const fromIndex = this._getItemIndex($(this._element).find(Selector.ACTIVE_ITEM)[0])
|
||||
const slideEvent = $.Event(Event.SLIDE, {
|
||||
relatedTarget,
|
||||
direction: eventDirectionName
|
||||
direction: eventDirectionName,
|
||||
from: fromIndex,
|
||||
to: targetIndex
|
||||
})
|
||||
|
||||
$(this._element).trigger(slideEvent)
|
||||
@@ -310,9 +312,10 @@ const Carousel = (($) => {
|
||||
|
||||
_slide(direction, element) {
|
||||
const activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]
|
||||
const activeElementIndex = this._getItemIndex(activeElement)
|
||||
const nextElement = element || activeElement &&
|
||||
this._getItemByDirection(direction, activeElement)
|
||||
|
||||
const nextElementIndex = this._getItemIndex(nextElement)
|
||||
const isCycling = Boolean(this._interval)
|
||||
|
||||
let directionalClassName
|
||||
@@ -354,7 +357,9 @@ const Carousel = (($) => {
|
||||
|
||||
const slidEvent = $.Event(Event.SLID, {
|
||||
relatedTarget: nextElement,
|
||||
direction: eventDirectionName
|
||||
direction: eventDirectionName,
|
||||
from: activeElementIndex,
|
||||
to: nextElementIndex
|
||||
})
|
||||
|
||||
if (Util.supportsTransitionEnd() &&
|
||||
|
@@ -57,7 +57,8 @@ const Collapse = (($) => {
|
||||
|
||||
const Selector = {
|
||||
ACTIVES : '.card > .show, .card > .collapsing',
|
||||
DATA_TOGGLE : '[data-toggle="collapse"]'
|
||||
DATA_TOGGLE : '[data-toggle="collapse"]',
|
||||
DATA_CHILDREN : 'data-children'
|
||||
}
|
||||
|
||||
|
||||
@@ -77,13 +78,20 @@ const Collapse = (($) => {
|
||||
`[data-toggle="collapse"][href="#${element.id}"],` +
|
||||
`[data-toggle="collapse"][data-target="#${element.id}"]`
|
||||
))
|
||||
|
||||
this._parent = this._config.parent ? this._getParent() : null
|
||||
|
||||
if (!this._config.parent) {
|
||||
this._addAriaAndCollapsedClass(this._element, this._triggerArray)
|
||||
}
|
||||
|
||||
this._selectorActives = Selector.ACTIVES
|
||||
if (this._parent) {
|
||||
const childrenSelector = this._parent.hasAttribute(Selector.DATA_CHILDREN) ? this._parent.getAttribute(Selector.DATA_CHILDREN) : null
|
||||
if (childrenSelector !== null) {
|
||||
this._selectorActives = `${childrenSelector} > .show, ${childrenSelector} > .collapsing`
|
||||
}
|
||||
}
|
||||
|
||||
if (this._config.toggle) {
|
||||
this.toggle()
|
||||
}
|
||||
@@ -112,11 +120,8 @@ const Collapse = (($) => {
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Collapse is transitioning')
|
||||
}
|
||||
|
||||
if ($(this._element).hasClass(ClassName.SHOW)) {
|
||||
if (this._isTransitioning ||
|
||||
$(this._element).hasClass(ClassName.SHOW)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -124,7 +129,7 @@ const Collapse = (($) => {
|
||||
let activesData
|
||||
|
||||
if (this._parent) {
|
||||
actives = $.makeArray($(this._parent).find(Selector.ACTIVES))
|
||||
actives = $.makeArray($(this._parent).find(this._selectorActives))
|
||||
if (!actives.length) {
|
||||
actives = null
|
||||
}
|
||||
@@ -196,11 +201,8 @@ const Collapse = (($) => {
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Collapse is transitioning')
|
||||
}
|
||||
|
||||
if (!$(this._element).hasClass(ClassName.SHOW)) {
|
||||
if (this._isTransitioning ||
|
||||
!$(this._element).hasClass(ClassName.SHOW)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -211,10 +213,8 @@ const Collapse = (($) => {
|
||||
}
|
||||
|
||||
const dimension = this._getDimension()
|
||||
const offsetDimension = dimension === Dimension.WIDTH ?
|
||||
'offsetWidth' : 'offsetHeight'
|
||||
|
||||
this._element.style[dimension] = `${this._element[offsetDimension]}px`
|
||||
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
|
||||
|
||||
Util.reflow(this._element)
|
||||
|
||||
@@ -357,7 +357,9 @@ const Collapse = (($) => {
|
||||
*/
|
||||
|
||||
$(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
|
||||
event.preventDefault()
|
||||
if (!/input|textarea/i.test(event.target.tagName)) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
const target = Collapse._getTargetFromElement(this)
|
||||
const data = $(target).data(DATA_KEY)
|
||||
|
@@ -96,16 +96,6 @@ const Dropdown = (($) => {
|
||||
return false
|
||||
}
|
||||
|
||||
if ('ontouchstart' in document.documentElement &&
|
||||
!$(parent).closest(Selector.NAVBAR_NAV).length) {
|
||||
|
||||
// if mobile we use a backdrop because click events don't delegate
|
||||
const dropdown = document.createElement('div')
|
||||
dropdown.className = ClassName.BACKDROP
|
||||
$(dropdown).insertBefore(this)
|
||||
$(dropdown).on('click', Dropdown._clearMenus)
|
||||
}
|
||||
|
||||
const relatedTarget = {
|
||||
relatedTarget : this
|
||||
}
|
||||
@@ -117,6 +107,17 @@ const Dropdown = (($) => {
|
||||
return false
|
||||
}
|
||||
|
||||
// set the backdrop only if the dropdown menu will be opened
|
||||
if ('ontouchstart' in document.documentElement &&
|
||||
!$(parent).closest(Selector.NAVBAR_NAV).length) {
|
||||
|
||||
// if mobile we use a backdrop because click events don't delegate
|
||||
const dropdown = document.createElement('div')
|
||||
dropdown.className = ClassName.BACKDROP
|
||||
$(dropdown).insertBefore(this)
|
||||
$(dropdown).on('click', Dropdown._clearMenus)
|
||||
}
|
||||
|
||||
this.focus()
|
||||
this.setAttribute('aria-expanded', true)
|
||||
|
||||
@@ -166,11 +167,6 @@ const Dropdown = (($) => {
|
||||
return
|
||||
}
|
||||
|
||||
const backdrop = $(Selector.BACKDROP)[0]
|
||||
if (backdrop) {
|
||||
backdrop.parentNode.removeChild(backdrop)
|
||||
}
|
||||
|
||||
const toggles = $.makeArray($(Selector.DATA_TOGGLE))
|
||||
|
||||
for (let i = 0; i < toggles.length; i++) {
|
||||
@@ -195,6 +191,12 @@ const Dropdown = (($) => {
|
||||
continue
|
||||
}
|
||||
|
||||
// remove backdrop only if the dropdown menu will be hidden
|
||||
const backdrop = $(parent).find(Selector.BACKDROP)[0]
|
||||
if (backdrop) {
|
||||
backdrop.parentNode.removeChild(backdrop)
|
||||
}
|
||||
|
||||
toggles[i].setAttribute('aria-expanded', 'false')
|
||||
|
||||
$(parent)
|
||||
|
@@ -67,7 +67,8 @@ const Modal = (($) => {
|
||||
DIALOG : '.modal-dialog',
|
||||
DATA_TOGGLE : '[data-toggle="modal"]',
|
||||
DATA_DISMISS : '[data-dismiss="modal"]',
|
||||
FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
|
||||
FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
|
||||
NAVBAR_TOGGLER : '.navbar-toggler'
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +88,6 @@ const Modal = (($) => {
|
||||
this._isShown = false
|
||||
this._isBodyOverflowing = false
|
||||
this._ignoreBackdropClick = false
|
||||
this._isTransitioning = false
|
||||
this._originalBodyPadding = 0
|
||||
this._scrollbarWidth = 0
|
||||
}
|
||||
@@ -112,13 +112,13 @@ const Modal = (($) => {
|
||||
|
||||
show(relatedTarget) {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Modal is transitioning')
|
||||
return
|
||||
}
|
||||
|
||||
if (Util.supportsTransitionEnd() &&
|
||||
$(this._element).hasClass(ClassName.FADE)) {
|
||||
if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
const showEvent = $.Event(Event.SHOW, {
|
||||
relatedTarget
|
||||
})
|
||||
@@ -161,17 +161,18 @@ const Modal = (($) => {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Modal is transitioning')
|
||||
if (this._isTransitioning || !this._isShown) {
|
||||
return
|
||||
}
|
||||
|
||||
const transition = Util.supportsTransitionEnd() &&
|
||||
$(this._element).hasClass(ClassName.FADE)
|
||||
const transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE)
|
||||
|
||||
if (transition) {
|
||||
this._isTransitioning = true
|
||||
}
|
||||
|
||||
const hideEvent = $.Event(Event.HIDE)
|
||||
|
||||
$(this._element).trigger(hideEvent)
|
||||
|
||||
if (!this._isShown || hideEvent.isDefaultPrevented()) {
|
||||
@@ -191,6 +192,7 @@ const Modal = (($) => {
|
||||
$(this._dialog).off(Event.MOUSEDOWN_DISMISS)
|
||||
|
||||
if (transition) {
|
||||
|
||||
$(this._element)
|
||||
.one(Util.TRANSITION_END, (event) => this._hideModal(event))
|
||||
.emulateTransitionEnd(TRANSITION_DURATION)
|
||||
@@ -211,10 +213,12 @@ const Modal = (($) => {
|
||||
this._isShown = null
|
||||
this._isBodyOverflowing = null
|
||||
this._ignoreBackdropClick = null
|
||||
this._originalBodyPadding = null
|
||||
this._scrollbarWidth = null
|
||||
}
|
||||
|
||||
handleUpdate() {
|
||||
this._adjustDialog()
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
@@ -296,7 +300,7 @@ const Modal = (($) => {
|
||||
|
||||
_setResizeEvent() {
|
||||
if (this._isShown) {
|
||||
$(window).on(Event.RESIZE, (event) => this._handleUpdate(event))
|
||||
$(window).on(Event.RESIZE, (event) => this.handleUpdate(event))
|
||||
} else {
|
||||
$(window).off(Event.RESIZE)
|
||||
}
|
||||
@@ -304,7 +308,7 @@ const Modal = (($) => {
|
||||
|
||||
_hideModal() {
|
||||
this._element.style.display = 'none'
|
||||
this._element.setAttribute('aria-hidden', 'true')
|
||||
this._element.setAttribute('aria-hidden', true)
|
||||
this._isTransitioning = false
|
||||
this._showBackdrop(() => {
|
||||
$(document.body).removeClass(ClassName.OPEN)
|
||||
@@ -401,10 +405,6 @@ const Modal = (($) => {
|
||||
// todo (fat): these should probably be refactored out of modal.js
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
_handleUpdate() {
|
||||
this._adjustDialog()
|
||||
}
|
||||
|
||||
_adjustDialog() {
|
||||
const isModalOverflowing =
|
||||
this._element.scrollHeight > document.documentElement.clientHeight
|
||||
@@ -429,28 +429,60 @@ const Modal = (($) => {
|
||||
}
|
||||
|
||||
_setScrollbar() {
|
||||
const bodyPadding = parseInt(
|
||||
$(Selector.FIXED_CONTENT).css('padding-right') || 0,
|
||||
10
|
||||
)
|
||||
|
||||
this._originalBodyPadding = document.body.style.paddingRight || ''
|
||||
|
||||
if (this._isBodyOverflowing) {
|
||||
document.body.style.paddingRight =
|
||||
`${bodyPadding + this._scrollbarWidth}px`
|
||||
// Note: DOMNode.style.paddingRight returns the actual value or '' if not set
|
||||
// while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
|
||||
|
||||
// Adjust fixed content padding
|
||||
$(Selector.FIXED_CONTENT).each((index, element) => {
|
||||
const actualPadding = $(element)[0].style.paddingRight
|
||||
const calculatedPadding = $(element).css('padding-right')
|
||||
$(element).data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
|
||||
})
|
||||
|
||||
// Adjust navbar-toggler margin
|
||||
$(Selector.NAVBAR_TOGGLER).each((index, element) => {
|
||||
const actualMargin = $(element)[0].style.marginRight
|
||||
const calculatedMargin = $(element).css('margin-right')
|
||||
$(element).data('margin-right', actualMargin).css('margin-right', `${parseFloat(calculatedMargin) + this._scrollbarWidth}px`)
|
||||
})
|
||||
|
||||
// Adjust body padding
|
||||
const actualPadding = document.body.style.paddingRight
|
||||
const calculatedPadding = $('body').css('padding-right')
|
||||
$('body').data('padding-right', actualPadding).css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)
|
||||
}
|
||||
}
|
||||
|
||||
_resetScrollbar() {
|
||||
document.body.style.paddingRight = this._originalBodyPadding
|
||||
// Restore fixed content padding
|
||||
$(Selector.FIXED_CONTENT).each((index, element) => {
|
||||
const padding = $(element).data('padding-right')
|
||||
if (typeof padding !== 'undefined') {
|
||||
$(element).css('padding-right', padding).removeData('padding-right')
|
||||
}
|
||||
})
|
||||
|
||||
// Restore navbar-toggler margin
|
||||
$(Selector.NAVBAR_TOGGLER).each((index, element) => {
|
||||
const margin = $(element).data('margin-right')
|
||||
if (typeof margin !== 'undefined') {
|
||||
$(element).css('margin-right', margin).removeData('margin-right')
|
||||
}
|
||||
})
|
||||
|
||||
// Restore body padding
|
||||
const padding = $('body').data('padding-right')
|
||||
if (typeof padding !== 'undefined') {
|
||||
$('body').css('padding-right', padding).removeData('padding-right')
|
||||
}
|
||||
}
|
||||
|
||||
_getScrollbarWidth() { // thx d.walsh
|
||||
const scrollDiv = document.createElement('div')
|
||||
scrollDiv.className = ClassName.SCROLLBAR_MEASURER
|
||||
document.body.appendChild(scrollDiv)
|
||||
const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
|
||||
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
|
||||
document.body.removeChild(scrollDiv)
|
||||
return scrollbarWidth
|
||||
}
|
||||
|
@@ -45,18 +45,15 @@ const ScrollSpy = (($) => {
|
||||
const ClassName = {
|
||||
DROPDOWN_ITEM : 'dropdown-item',
|
||||
DROPDOWN_MENU : 'dropdown-menu',
|
||||
NAV_LINK : 'nav-link',
|
||||
NAV : 'nav',
|
||||
ACTIVE : 'active'
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
DATA_SPY : '[data-spy="scroll"]',
|
||||
ACTIVE : '.active',
|
||||
LIST_ITEM : '.list-item',
|
||||
LI : 'li',
|
||||
LI_DROPDOWN : 'li.dropdown',
|
||||
NAV_LIST_GROUP : '.nav, .list-group',
|
||||
NAV_LINKS : '.nav-link',
|
||||
LIST_ITEMS : '.list-group-item',
|
||||
DROPDOWN : '.dropdown',
|
||||
DROPDOWN_ITEMS : '.dropdown-item',
|
||||
DROPDOWN_TOGGLE : '.dropdown-toggle'
|
||||
@@ -81,6 +78,7 @@ const ScrollSpy = (($) => {
|
||||
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} ${Selector.DROPDOWN_ITEMS}`
|
||||
this._offsets = []
|
||||
this._targets = []
|
||||
@@ -133,12 +131,15 @@ const ScrollSpy = (($) => {
|
||||
target = $(targetSelector)[0]
|
||||
}
|
||||
|
||||
if (target && (target.offsetWidth || target.offsetHeight)) {
|
||||
// todo (fat): remove sketch reliance on jQuery position/offset
|
||||
return [
|
||||
$(target)[offsetMethod]().top + offsetBase,
|
||||
targetSelector
|
||||
]
|
||||
if (target) {
|
||||
const targetBCR = target.getBoundingClientRect()
|
||||
if (targetBCR.width || targetBCR.height) {
|
||||
// todo (fat): remove sketch reliance on jQuery position/offset
|
||||
return [
|
||||
$(target)[offsetMethod]().top + offsetBase,
|
||||
targetSelector
|
||||
]
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
@@ -198,7 +199,7 @@ const ScrollSpy = (($) => {
|
||||
|
||||
_getOffsetHeight() {
|
||||
return this._scrollElement === window ?
|
||||
window.innerHeight : this._scrollElement.offsetHeight
|
||||
window.innerHeight : this._scrollElement.getBoundingClientRect().height
|
||||
}
|
||||
|
||||
_process() {
|
||||
@@ -256,9 +257,11 @@ const ScrollSpy = (($) => {
|
||||
$link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
|
||||
$link.addClass(ClassName.ACTIVE)
|
||||
} else {
|
||||
// todo (fat) this is kinda sus...
|
||||
// recursively add actives to tested nav-links
|
||||
$link.parents(Selector.LI).find(`> ${Selector.NAV_LINKS}`).addClass(ClassName.ACTIVE)
|
||||
// Set triggered link as active
|
||||
$link.addClass(ClassName.ACTIVE)
|
||||
// Set triggered links parents as active
|
||||
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
|
||||
$link.parents(Selector.NAV_LIST_GROUP).prev(`${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`).addClass(ClassName.ACTIVE)
|
||||
}
|
||||
|
||||
$(this._scrollElement).trigger(Event.ACTIVATE, {
|
||||
|
@@ -42,14 +42,10 @@ const Tab = (($) => {
|
||||
}
|
||||
|
||||
const Selector = {
|
||||
A : 'a',
|
||||
LI : 'li',
|
||||
DROPDOWN : '.dropdown',
|
||||
LIST : 'ul:not(.dropdown-menu), ol:not(.dropdown-menu), nav:not(.dropdown-menu)',
|
||||
FADE_CHILD : '> .nav-item .fade, > .fade',
|
||||
NAV_LIST_GROUP : '.nav, .list-group',
|
||||
ACTIVE : '.active',
|
||||
ACTIVE_CHILD : '> .nav-item > .active, > .active',
|
||||
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"]',
|
||||
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
|
||||
DROPDOWN_TOGGLE : '.dropdown-toggle',
|
||||
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
|
||||
}
|
||||
@@ -87,7 +83,7 @@ const Tab = (($) => {
|
||||
|
||||
let target
|
||||
let previous
|
||||
const listElement = $(this._element).closest(Selector.LIST)[0]
|
||||
const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
|
||||
const selector = Util.getSelectorFromElement(this._element)
|
||||
|
||||
if (listElement) {
|
||||
@@ -152,11 +148,10 @@ const Tab = (($) => {
|
||||
// private
|
||||
|
||||
_activate(element, container, callback) {
|
||||
const active = $(container).find(Selector.ACTIVE_CHILD)[0]
|
||||
const active = $(container).find(Selector.ACTIVE)[0]
|
||||
const isTransitioning = callback
|
||||
&& Util.supportsTransitionEnd()
|
||||
&& (active && $(active).hasClass(ClassName.FADE)
|
||||
|| Boolean($(container).find(Selector.FADE_CHILD)[0]))
|
||||
&& (active && $(active).hasClass(ClassName.FADE))
|
||||
|
||||
const complete = () => this._transitionComplete(
|
||||
element,
|
||||
|
@@ -34,6 +34,7 @@ const Tooltip = (($) => {
|
||||
const JQUERY_NO_CONFLICT = $.fn[NAME]
|
||||
const TRANSITION_DURATION = 150
|
||||
const CLASS_PREFIX = 'bs-tether'
|
||||
const TETHER_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
|
||||
|
||||
const Default = {
|
||||
animation : true,
|
||||
@@ -123,12 +124,11 @@ const Tooltip = (($) => {
|
||||
constructor(element, config) {
|
||||
|
||||
// private
|
||||
this._isEnabled = true
|
||||
this._timeout = 0
|
||||
this._hoverState = ''
|
||||
this._activeTrigger = {}
|
||||
this._isTransitioning = false
|
||||
this._tether = null
|
||||
this._isEnabled = true
|
||||
this._timeout = 0
|
||||
this._hoverState = ''
|
||||
this._activeTrigger = {}
|
||||
this._tether = null
|
||||
|
||||
// protected
|
||||
this.element = element
|
||||
@@ -249,9 +249,6 @@ const Tooltip = (($) => {
|
||||
|
||||
const showEvent = $.Event(this.constructor.Event.SHOW)
|
||||
if (this.isWithContent() && this._isEnabled) {
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Tooltip is transitioning')
|
||||
}
|
||||
$(this.element).trigger(showEvent)
|
||||
|
||||
const isInTheDom = $.contains(
|
||||
@@ -283,9 +280,11 @@ const Tooltip = (($) => {
|
||||
|
||||
const container = this.config.container === false ? document.body : $(this.config.container)
|
||||
|
||||
$(tip)
|
||||
.data(this.constructor.DATA_KEY, this)
|
||||
.appendTo(container)
|
||||
$(tip).data(this.constructor.DATA_KEY, this)
|
||||
|
||||
if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
|
||||
$(tip).appendTo(container)
|
||||
}
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.INSERTED)
|
||||
|
||||
@@ -307,8 +306,7 @@ const Tooltip = (($) => {
|
||||
|
||||
const complete = () => {
|
||||
const prevHoverState = this._hoverState
|
||||
this._hoverState = null
|
||||
this._isTransitioning = false
|
||||
this._hoverState = null
|
||||
|
||||
$(this.element).trigger(this.constructor.Event.SHOWN)
|
||||
|
||||
@@ -318,7 +316,6 @@ const Tooltip = (($) => {
|
||||
}
|
||||
|
||||
if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
$(this.tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(Tooltip._TRANSITION_DURATION)
|
||||
@@ -332,17 +329,14 @@ const Tooltip = (($) => {
|
||||
hide(callback) {
|
||||
const tip = this.getTipElement()
|
||||
const hideEvent = $.Event(this.constructor.Event.HIDE)
|
||||
if (this._isTransitioning) {
|
||||
throw new Error('Tooltip is transitioning')
|
||||
}
|
||||
const complete = () => {
|
||||
if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
|
||||
tip.parentNode.removeChild(tip)
|
||||
}
|
||||
|
||||
this._cleanTipClass()
|
||||
this.element.removeAttribute('aria-describedby')
|
||||
$(this.element).trigger(this.constructor.Event.HIDDEN)
|
||||
this._isTransitioning = false
|
||||
this.cleanupTether()
|
||||
|
||||
if (callback) {
|
||||
@@ -364,7 +358,7 @@ const Tooltip = (($) => {
|
||||
|
||||
if (Util.supportsTransitionEnd() &&
|
||||
$(this.tip).hasClass(ClassName.FADE)) {
|
||||
this._isTransitioning = true
|
||||
|
||||
$(tip)
|
||||
.one(Util.TRANSITION_END, complete)
|
||||
.emulateTransitionEnd(TRANSITION_DURATION)
|
||||
@@ -438,6 +432,14 @@ const Tooltip = (($) => {
|
||||
return AttachmentMap[placement.toUpperCase()]
|
||||
}
|
||||
|
||||
_cleanTipClass() {
|
||||
const $tip = $(this.getTipElement())
|
||||
const tabClass = $tip.attr('class').match(TETHER_PREFIX_REGEX)
|
||||
if (tabClass !== null && tabClass.length > 0) {
|
||||
$tip.removeClass(tabClass.join(''))
|
||||
}
|
||||
}
|
||||
|
||||
_setListeners() {
|
||||
const triggers = this.config.trigger.split(' ')
|
||||
|
||||
@@ -603,6 +605,14 @@ const Tooltip = (($) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.title && typeof config.title === 'number') {
|
||||
config.title = config.title.toString()
|
||||
}
|
||||
|
||||
if (config.content && typeof config.content === 'number') {
|
||||
config.content = config.content.toString()
|
||||
}
|
||||
|
||||
Util.typeCheckConfig(
|
||||
NAME,
|
||||
config,
|
||||
|
@@ -112,13 +112,16 @@ const Util = (($) => {
|
||||
|
||||
getSelectorFromElement(element) {
|
||||
let selector = element.getAttribute('data-target')
|
||||
|
||||
if (!selector) {
|
||||
if (!selector || selector === '#') {
|
||||
selector = element.getAttribute('href') || ''
|
||||
selector = /^#[a-z]/i.test(selector) ? selector : null
|
||||
}
|
||||
|
||||
return selector
|
||||
try {
|
||||
const $selector = $(selector)
|
||||
return $selector.length > 0 ? selector : null
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
reflow(element) {
|
||||
|
Reference in New Issue
Block a user