tab.js 6.8 KB
Newer Older
1
import $ from 'jquery'
fat's avatar
tab es6  
fat committed
2
3
4
5
import Util from './util'

/**
 * --------------------------------------------------------------------------
Mark Otto's avatar
Mark Otto committed
6
 * Bootstrap (v4.0.0): tab.js
fat's avatar
tab es6  
fat committed
7
8
9
10
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

11
const Tab = (($) => {
fat's avatar
tab es6  
fat committed
12
13
14
15
16
17
  /**
   * ------------------------------------------------------------------------
   * Constants
   * ------------------------------------------------------------------------
   */

18
19
20
21
22
23
  const NAME               = 'tab'
  const VERSION            = '4.0.0'
  const DATA_KEY           = 'bs.tab'
  const EVENT_KEY          = `.${DATA_KEY}`
  const DATA_API_KEY       = '.data-api'
  const JQUERY_NO_CONFLICT = $.fn[NAME]
fat's avatar
tab es6  
fat committed
24
25

  const Event = {
fat's avatar
fat committed
26
27
28
29
30
    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}`
fat's avatar
tab es6  
fat committed
31
32
33
34
35
  }

  const ClassName = {
    DROPDOWN_MENU : 'dropdown-menu',
    ACTIVE        : 'active',
36
    DISABLED      : 'disabled',
fat's avatar
tab es6  
fat committed
37
    FADE          : 'fade',
Starsam80's avatar
Starsam80 committed
38
    SHOW          : 'show'
fat's avatar
tab es6  
fat committed
39
40
41
  }

  const Selector = {
42
    DROPDOWN              : '.dropdown',
43
    NAV_LIST_GROUP        : '.nav, .list-group',
44
    ACTIVE                : '.active',
45
    ACTIVE_UL             : '> li > .active',
46
    DATA_TOGGLE           : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
47
48
    DROPDOWN_TOGGLE       : '.dropdown-toggle',
    DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
fat's avatar
tab es6  
fat committed
49
50
51
52
53
54
55
56
57
58
59
60
61
  }

  /**
   * ------------------------------------------------------------------------
   * Class Definition
   * ------------------------------------------------------------------------
   */

  class Tab {
    constructor(element) {
      this._element = element
    }

XhmikosR's avatar
XhmikosR committed
62
    // Getters
fat's avatar
tab es6  
fat committed
63
64
65
66
67

    static get VERSION() {
      return VERSION
    }

XhmikosR's avatar
XhmikosR committed
68
    // Public
fat's avatar
tab es6  
fat committed
69
70
71

    show() {
      if (this._element.parentNode &&
72
          this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
73
74
          $(this._element).hasClass(ClassName.ACTIVE) ||
          $(this._element).hasClass(ClassName.DISABLED)) {
fat's avatar
tab es6  
fat committed
75
76
77
78
79
        return
      }

      let target
      let previous
80
      const listElement = $(this._element).closest(Selector.NAV_LIST_GROUP)[0]
XhmikosR's avatar
XhmikosR committed
81
      const selector = Util.getSelectorFromElement(this._element)
fat's avatar
tab es6  
fat committed
82

83
      if (listElement) {
84
85
        const itemSelector = listElement.nodeName === 'UL' ? Selector.ACTIVE_UL : Selector.ACTIVE
        previous = $.makeArray($(listElement).find(itemSelector))
fat's avatar
tab es6  
fat committed
86
87
88
        previous = previous[previous.length - 1]
      }

89
      const hideEvent = $.Event(Event.HIDE, {
fat's avatar
tab es6  
fat committed
90
91
92
        relatedTarget: this._element
      })

93
      const showEvent = $.Event(Event.SHOW, {
fat's avatar
tab es6  
fat committed
94
95
96
97
98
99
100
101
102
103
        relatedTarget: previous
      })

      if (previous) {
        $(previous).trigger(hideEvent)
      }

      $(this._element).trigger(showEvent)

      if (showEvent.isDefaultPrevented() ||
104
         hideEvent.isDefaultPrevented()) {
fat's avatar
tab es6  
fat committed
105
106
107
108
109
110
111
112
        return
      }

      if (selector) {
        target = $(selector)[0]
      }

      this._activate(
113
        this._element,
114
        listElement
fat's avatar
tab es6  
fat committed
115
116
      )

117
118
      const complete = () => {
        const hiddenEvent = $.Event(Event.HIDDEN, {
fat's avatar
tab es6  
fat committed
119
120
121
          relatedTarget: this._element
        })

122
        const shownEvent = $.Event(Event.SHOWN, {
fat's avatar
tab es6  
fat committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
          relatedTarget: previous
        })

        $(previous).trigger(hiddenEvent)
        $(this._element).trigger(shownEvent)
      }

      if (target) {
        this._activate(target, target.parentNode, complete)
      } else {
        complete()
      }
    }

fat's avatar
fat committed
137
    dispose() {
138
      $.removeData(this._element, DATA_KEY)
fat's avatar
fat committed
139
140
141
      this._element = null
    }

XhmikosR's avatar
XhmikosR committed
142
    // Private
fat's avatar
tab es6  
fat committed
143
144

    _activate(element, container, callback) {
145
146
147
148
149
150
151
      let activeElements
      if (container.nodeName === 'UL') {
        activeElements = $(container).find(Selector.ACTIVE_UL)
      } else {
        activeElements = $(container).children(Selector.ACTIVE)
      }

XhmikosR's avatar
XhmikosR committed
152
153
154
155
      const active = activeElements[0]
      const isTransitioning = callback &&
        Util.supportsTransitionEnd() &&
        (active && $(active).hasClass(ClassName.FADE))
fat's avatar
tab es6  
fat committed
156

157
      const complete = () => this._transitionComplete(
158
159
160
161
        element,
        active,
        callback
      )
fat's avatar
tab es6  
fat committed
162
163

      if (active && isTransitioning) {
164
165
        const transitionDuration = Util.getTransitionDurationFromElement(active)

fat's avatar
tab es6  
fat committed
166
167
        $(active)
          .one(Util.TRANSITION_END, complete)
168
          .emulateTransitionEnd(transitionDuration)
fat's avatar
tab es6  
fat committed
169
170
171
172
173
      } else {
        complete()
      }
    }

174
    _transitionComplete(element, active, callback) {
fat's avatar
tab es6  
fat committed
175
      if (active) {
176
        $(active).removeClass(`${ClassName.SHOW} ${ClassName.ACTIVE}`)
fat's avatar
tab es6  
fat committed
177

178
        const dropdownChild = $(active.parentNode).find(
fat's avatar
tab es6  
fat committed
179
180
          Selector.DROPDOWN_ACTIVE_CHILD
        )[0]
181

fat's avatar
tab es6  
fat committed
182
183
184
185
        if (dropdownChild) {
          $(dropdownChild).removeClass(ClassName.ACTIVE)
        }

186
187
188
        if (active.getAttribute('role') === 'tab') {
          active.setAttribute('aria-selected', false)
        }
fat's avatar
tab es6  
fat committed
189
190
191
      }

      $(element).addClass(ClassName.ACTIVE)
192
193
194
      if (element.getAttribute('role') === 'tab') {
        element.setAttribute('aria-selected', true)
      }
fat's avatar
tab es6  
fat committed
195

196
197
      Util.reflow(element)
      $(element).addClass(ClassName.SHOW)
fat's avatar
tab es6  
fat committed
198
199

      if (element.parentNode &&
200
201
          $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
        const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
fat's avatar
tab es6  
fat committed
202
        if (dropdownElement) {
203
          $(dropdownElement).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
fat's avatar
tab es6  
fat committed
204
205
        }

206
        element.setAttribute('aria-expanded', true)
fat's avatar
tab es6  
fat committed
207
208
209
210
211
212
213
      }

      if (callback) {
        callback()
      }
    }

XhmikosR's avatar
XhmikosR committed
214
    // Static
fat's avatar
tab es6  
fat committed
215
216
217

    static _jQueryInterface(config) {
      return this.each(function () {
218
        const $this = $(this)
XhmikosR's avatar
XhmikosR committed
219
        let data = $this.data(DATA_KEY)
fat's avatar
tab es6  
fat committed
220
221

        if (!data) {
222
          data = new Tab(this)
fat's avatar
tab es6  
fat committed
223
224
225
226
          $this.data(DATA_KEY, data)
        }

        if (typeof config === 'string') {
XhmikosR's avatar
XhmikosR committed
227
          if (typeof data[config] === 'undefined') {
XhmikosR's avatar
XhmikosR committed
228
            throw new TypeError(`No method named "${config}"`)
229
          }
fat's avatar
tab es6  
fat committed
230
231
232
233
234
235
236
237
238
239
240
241
242
          data[config]()
        }
      })
    }
  }

  /**
   * ------------------------------------------------------------------------
   * Data Api implementation
   * ------------------------------------------------------------------------
   */

  $(document)
fat's avatar
fat committed
243
    .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
XhmikosR's avatar
XhmikosR committed
244
245
246
      event.preventDefault()
      Tab._jQueryInterface.call($(this), 'show')
    })
fat's avatar
tab es6  
fat committed
247
248
249
250
251
252
253

  /**
   * ------------------------------------------------------------------------
   * jQuery
   * ------------------------------------------------------------------------
   */

XhmikosR's avatar
XhmikosR committed
254
  $.fn[NAME] = Tab._jQueryInterface
fat's avatar
tab es6  
fat committed
255
  $.fn[NAME].Constructor = Tab
XhmikosR's avatar
XhmikosR committed
256
  $.fn[NAME].noConflict = function () {
fat's avatar
tab es6  
fat committed
257
258
259
260
261
    $.fn[NAME] = JQUERY_NO_CONFLICT
    return Tab._jQueryInterface
  }

  return Tab
262
})($)
fat's avatar
tab es6  
fat committed
263
264

export default Tab