2 import Util from './util'
5 * --------------------------------------------------------------------------
6 * Bootstrap (v4.1.3): tab.js
7 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8 * --------------------------------------------------------------------------
13 * ------------------------------------------------------------------------
15 * ------------------------------------------------------------------------
19 const VERSION = '4.1.3'
20 const DATA_KEY = 'bs.tab'
21 const EVENT_KEY = `.${DATA_KEY}`
22 const DATA_API_KEY = '.data-api'
23 const JQUERY_NO_CONFLICT = $.fn[NAME]
26 HIDE : `hide${EVENT_KEY}`,
27 HIDDEN : `hidden${EVENT_KEY}`,
28 SHOW : `show${EVENT_KEY}`,
29 SHOWN : `shown${EVENT_KEY}`,
30 CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
34 DROPDOWN_MENU : 'dropdown-menu',
36 DISABLED : 'disabled',
42 DROPDOWN : '.dropdown',
43 NAV_LIST_GROUP : '.nav, .list-group',
45 ACTIVE_UL : '> li > .active',
46 DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
47 DROPDOWN_TOGGLE : '.dropdown-toggle',
48 DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
52 * ------------------------------------------------------------------------
54 * ------------------------------------------------------------------------
58 constructor(element) {
59 this._element = element
64 static get VERSION() {
71 if (this._element.parentNode &&
72 this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
73 $(this._element).hasClass(ClassName.ACTIVE) ||
74 $(this._element).hasClass(ClassName.DISABLED)) {
80 const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
81 const selector = Util.getSelectorFromElement(this._element)
84 const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
85 previous = $.makeArray($(listElement).find(itemSelector))
86 previous = previous[previous.length - 1]
89 const hideEvent = $.Event(Event.HIDE, {
90 relatedTarget: this._element
93 const showEvent = $.Event(Event.SHOW, {
94 relatedTarget: previous
98 $(previous).trigger(hideEvent)
101 $(this._element).trigger(showEvent)
103 if (showEvent.isDefaultPrevented() ||
104 hideEvent.isDefaultPrevented()) {
109 target = document.querySelector(selector)
117 const complete = () => {
118 const hiddenEvent = $.Event(Event.HIDDEN, {
119 relatedTarget: this._element
122 const shownEvent = $.Event(Event.SHOWN, {
123 relatedTarget: previous
126 $(previous).trigger(hiddenEvent)
127 $(this._element).trigger(shownEvent)
131 this._activate(target, target.parentNode, complete)
138 $.removeData(this._element, DATA_KEY)
144 _activate(element, container, callback) {
146 if (container.nodeName === 'UL') {
147 activeElements = $(container).find(Selector.ACTIVE_UL)
149 activeElements = $(container).children(Selector.ACTIVE)
152 const active = activeElements[0]
153 const isTransitioning = callback &&
154 (active && $(active).hasClass(ClassName.FADE))
156 const complete = () => this._transitionComplete(
162 if (active && isTransitioning) {
163 const transitionDuration = Util.getTransitionDurationFromElement(active)
166 .one(Util.TRANSITION_END, complete)
167 .emulateTransitionEnd(transitionDuration)
173 _transitionComplete(element, active, callback) {
175 $(active).removeClass(`${ClassName.SHOW} ${ClassName.ACTIVE}`)
177 const dropdownChild = $(active.parentNode).find(
178 Selector.DROPDOWN_ACTIVE_CHILD
182 $(dropdownChild).removeClass(ClassName.ACTIVE)
185 if (active.getAttribute('role') === 'tab') {
186 active.setAttribute('aria-selected', false)
190 $(element).addClass(ClassName.ACTIVE)
191 if (element.getAttribute('role') === 'tab') {
192 element.setAttribute('aria-selected', true)
196 $(element).addClass(ClassName.SHOW)
198 if (element.parentNode &&
199 $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
200 const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
201 if (dropdownElement) {
202 const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(Selector.DROPDOWN_TOGGLE))
203 $(dropdownToggleList).addClass(ClassName.ACTIVE)
206 element.setAttribute('aria-expanded', true)
216 static _jQueryInterface(config) {
217 return this.each(function () {
218 const $this = $(this)
219 let data = $this.data(DATA_KEY)
223 $this.data(DATA_KEY, data)
226 if (typeof config === 'string') {
227 if (typeof data[config] === 'undefined') {
228 throw new TypeError(`No method named "${config}"`)
237 * ------------------------------------------------------------------------
238 * Data Api implementation
239 * ------------------------------------------------------------------------
243 .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
244 event.preventDefault()
245 Tab._jQueryInterface.call($(this), 'show')
249 * ------------------------------------------------------------------------
251 * ------------------------------------------------------------------------
254 $.fn[NAME] = Tab._jQueryInterface
255 $.fn[NAME].Constructor = Tab
256 $.fn[NAME].noConflict = function () {
257 $.fn[NAME] = JQUERY_NO_CONFLICT
258 return Tab._jQueryInterface