0
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-02-03 01:52:19 +01:00
Bootstrap/js/src/tab.js

262 lines
6.4 KiB
JavaScript
Raw Normal View History

import $ from 'jquery'
2015-05-11 12:29:06 -07:00
import Util from './util'
/**
* --------------------------------------------------------------------------
2018-07-23 19:25:52 -07:00
* Bootstrap (v4.1.3): tab.js
2015-05-11 12:29:06 -07:00
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
2018-09-26 10:39:01 +02:00
/**
* ------------------------------------------------------------------------
* Constants
* ------------------------------------------------------------------------
*/
const NAME = 'tab'
const VERSION = '4.1.3'
const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const JQUERY_NO_CONFLICT = $.fn[NAME]
const Event = {
HIDE : `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = {
DROPDOWN_MENU : 'dropdown-menu',
ACTIVE : 'active',
DISABLED : 'disabled',
FADE : 'fade',
SHOW : 'show'
}
const Selector = {
DROPDOWN : '.dropdown',
NAV_LIST_GROUP : '.nav, .list-group',
ACTIVE : '.active',
ACTIVE_UL : '> li > .active',
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE : '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
/**
* ------------------------------------------------------------------------
* Class Definition
* ------------------------------------------------------------------------
*/
class Tab {
constructor(element) {
this._element = element
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
// Getters
static get VERSION() {
return VERSION
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
// Public
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
show() {
if (this._element.parentNode &&
this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
$(this._element).hasClass(ClassName.ACTIVE) ||
$(this._element).hasClass(ClassName.DISABLED)) {
return
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
let target
let previous
const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
const selector = Util.getSelectorFromElement(this._element)
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (listElement) {
const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
previous = $.makeArray($(listElement).find(itemSelector))
previous = previous[previous.length - 1]
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
const hideEvent = $.Event(Event.HIDE, {
relatedTarget: this._element
})
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
const showEvent = $.Event(Event.SHOW, {
relatedTarget: previous
})
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (previous) {
$(previous).trigger(hideEvent)
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
$(this._element).trigger(showEvent)
if (showEvent.isDefaultPrevented() ||
hideEvent.isDefaultPrevented()) {
return
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (selector) {
target = document.querySelector(selector)
}
this._activate(
this._element,
listElement
)
const complete = () => {
const hiddenEvent = $.Event(Event.HIDDEN, {
2015-05-11 12:29:06 -07:00
relatedTarget: this._element
})
2018-09-26 10:39:01 +02:00
const shownEvent = $.Event(Event.SHOWN, {
2015-05-11 12:29:06 -07:00
relatedTarget: previous
})
2018-09-26 10:39:01 +02:00
$(previous).trigger(hiddenEvent)
$(this._element).trigger(shownEvent)
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (target) {
this._activate(target, target.parentNode, complete)
} else {
complete()
}
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
dispose() {
$.removeData(this._element, DATA_KEY)
this._element = null
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
// Private
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
_activate(element, container, callback) {
let activeElements
if (container.nodeName === 'UL') {
activeElements = $(container).find(Selector.ACTIVE_UL)
} else {
activeElements = $(container).children(Selector.ACTIVE)
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
const active = activeElements[0]
const isTransitioning = callback &&
(active && $(active).hasClass(ClassName.FADE))
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
const complete = () => this._transitionComplete(
element,
active,
callback
)
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (active && isTransitioning) {
const transitionDuration = Util.getTransitionDurationFromElement(active)
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
$(active)
2018-10-29 09:54:45 +01:00
.removeClass(ClassName.SHOW)
2018-09-26 10:39:01 +02:00
.one(Util.TRANSITION_END, complete)
.emulateTransitionEnd(transitionDuration)
} else {
complete()
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
_transitionComplete(element, active, callback) {
if (active) {
2018-10-29 09:54:45 +01:00
$(active).removeClass(ClassName.ACTIVE)
2015-05-13 12:48:34 -07:00
2018-09-26 10:39:01 +02:00
const dropdownChild = $(active.parentNode).find(
Selector.DROPDOWN_ACTIVE_CHILD
)[0]
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (dropdownChild) {
$(dropdownChild).removeClass(ClassName.ACTIVE)
}
2018-09-26 10:39:01 +02:00
if (active.getAttribute('role') === 'tab') {
active.setAttribute('aria-selected', false)
2015-05-11 12:29:06 -07:00
}
}
2018-09-26 10:39:01 +02:00
$(element).addClass(ClassName.ACTIVE)
if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true)
}
2015-08-18 22:03:34 -07:00
2018-09-26 10:39:01 +02:00
Util.reflow(element)
$(element).addClass(ClassName.SHOW)
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (element.parentNode &&
$(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
if (dropdownElement) {
const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(Selector.DROPDOWN_TOGGLE))
$(dropdownToggleList).addClass(ClassName.ACTIVE)
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
element.setAttribute('aria-expanded', true)
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (callback) {
callback()
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
// Static
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
static _jQueryInterface(config) {
return this.each(function () {
const $this = $(this)
let data = $this.data(DATA_KEY)
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (!data) {
data = new Tab(this)
$this.data(DATA_KEY, data)
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`)
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
data[config]()
}
})
2015-05-11 12:29:06 -07:00
}
2018-09-26 10:39:01 +02:00
}
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
/**
* ------------------------------------------------------------------------
* Data Api implementation
* ------------------------------------------------------------------------
*/
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
$(document)
.on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
event.preventDefault()
Tab._jQueryInterface.call($(this), 'show')
})
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
*/
2015-05-11 12:29:06 -07:00
2018-09-26 10:39:01 +02:00
$.fn[NAME] = Tab._jQueryInterface
$.fn[NAME].Constructor = Tab
$.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Tab._jQueryInterface
}
2015-05-11 12:29:06 -07:00
export default Tab