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


/**
 * --------------------------------------------------------------------------
Mark Otto's avatar
Mark Otto committed
6
 * Bootstrap (v4.0.0-alpha.6): tab.js
fat's avatar
tab es6  
fat committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

const Tab = (($) => {


  /**
   * ------------------------------------------------------------------------
   * Constants
   * ------------------------------------------------------------------------
   */

  const NAME                = 'tab'
Mark Otto's avatar
Mark Otto committed
21
  const VERSION             = '4.0.0-alpha.6'
fat's avatar
tab es6  
fat committed
22
  const DATA_KEY            = 'bs.tab'
fat's avatar
fat committed
23
24
  const EVENT_KEY           = `.${DATA_KEY}`
  const DATA_API_KEY        = '.data-api'
fat's avatar
tab es6  
fat committed
25
26
27
28
  const JQUERY_NO_CONFLICT  = $.fn[NAME]
  const TRANSITION_DURATION = 150

  const Event = {
fat's avatar
fat committed
29
30
31
32
33
    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
34
35
36
37
38
  }

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

  const Selector = {
45
    DROPDOWN              : '.dropdown',
46
    NAV_LIST_GROUP        : '.nav, .list-group',
fat's avatar
tab es6  
fat committed
47
    ACTIVE                : '.active',
48
    DATA_TOGGLE           : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
49
50
    DROPDOWN_TOGGLE       : '.dropdown-toggle',
    DROPDOWN_ACTIVE_CHILD : '> .dropdown-menu .active'
fat's avatar
tab es6  
fat committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  }


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

  class Tab {

    constructor(element) {
      this._element = element
    }


    // getters

    static get VERSION() {
      return VERSION
    }


    // public

    show() {
      if (this._element.parentNode &&
78
          this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
79
80
          $(this._element).hasClass(ClassName.ACTIVE) ||
          $(this._element).hasClass(ClassName.DISABLED)) {
fat's avatar
tab es6  
fat committed
81
82
83
84
85
        return
      }

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

89
90
      if (listElement) {
        previous = $.makeArray($(listElement).find(Selector.ACTIVE))
fat's avatar
tab es6  
fat committed
91
92
93
        previous = previous[previous.length - 1]
      }

94
      const hideEvent = $.Event(Event.HIDE, {
fat's avatar
tab es6  
fat committed
95
96
97
        relatedTarget: this._element
      })

98
      const showEvent = $.Event(Event.SHOW, {
fat's avatar
tab es6  
fat committed
99
100
101
102
103
104
105
106
107
108
        relatedTarget: previous
      })

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

      $(this._element).trigger(showEvent)

      if (showEvent.isDefaultPrevented() ||
109
         hideEvent.isDefaultPrevented()) {
fat's avatar
tab es6  
fat committed
110
111
112
113
114
115
116
117
        return
      }

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

      this._activate(
118
        this._element,
119
        listElement
fat's avatar
tab es6  
fat committed
120
121
      )

122
123
      const complete = () => {
        const hiddenEvent = $.Event(Event.HIDDEN, {
fat's avatar
tab es6  
fat committed
124
125
126
          relatedTarget: this._element
        })

127
        const shownEvent = $.Event(Event.SHOWN, {
fat's avatar
tab es6  
fat committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
          relatedTarget: previous
        })

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

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

fat's avatar
fat committed
142
    dispose() {
143
      $.removeData(this._element, DATA_KEY)
fat's avatar
fat committed
144
145
146
      this._element = null
    }

fat's avatar
tab es6  
fat committed
147
148
149
150

    // private

    _activate(element, container, callback) {
151
      const active          = $(container).find(Selector.ACTIVE)[0]
152
      const isTransitioning = callback
fat's avatar
tab es6  
fat committed
153
        && Util.supportsTransitionEnd()
154
        && (active && $(active).hasClass(ClassName.FADE))
fat's avatar
tab es6  
fat committed
155

156
      const complete = () => this._transitionComplete(
157
158
159
160
161
        element,
        active,
        isTransitioning,
        callback
      )
fat's avatar
tab es6  
fat committed
162
163
164
165
166
167
168
169
170
171
172

      if (active && isTransitioning) {
        $(active)
          .one(Util.TRANSITION_END, complete)
          .emulateTransitionEnd(TRANSITION_DURATION)

      } else {
        complete()
      }

      if (active) {
Starsam80's avatar
Starsam80 committed
173
        $(active).removeClass(ClassName.SHOW)
fat's avatar
tab es6  
fat committed
174
175
176
177
178
179
180
      }
    }

    _transitionComplete(element, active, isTransitioning, callback) {
      if (active) {
        $(active).removeClass(ClassName.ACTIVE)

181
        const dropdownChild = $(active.parentNode).find(
fat's avatar
tab es6  
fat committed
182
183
          Selector.DROPDOWN_ACTIVE_CHILD
        )[0]
184

fat's avatar
tab es6  
fat committed
185
186
187
188
        if (dropdownChild) {
          $(dropdownChild).removeClass(ClassName.ACTIVE)
        }

189
        active.setAttribute('aria-expanded', false)
fat's avatar
tab es6  
fat committed
190
191
192
      }

      $(element).addClass(ClassName.ACTIVE)
193
      element.setAttribute('aria-expanded', true)
fat's avatar
tab es6  
fat committed
194
195
196

      if (isTransitioning) {
        Util.reflow(element)
Starsam80's avatar
Starsam80 committed
197
        $(element).addClass(ClassName.SHOW)
fat's avatar
tab es6  
fat committed
198
199
200
201
202
      } else {
        $(element).removeClass(ClassName.FADE)
      }

      if (element.parentNode &&
203
          $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) {
fat's avatar
tab es6  
fat committed
204

205
        const dropdownElement = $(element).closest(Selector.DROPDOWN)[0]
fat's avatar
tab es6  
fat committed
206
        if (dropdownElement) {
207
          $(dropdownElement).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE)
fat's avatar
tab es6  
fat committed
208
209
        }

210
        element.setAttribute('aria-expanded', true)
fat's avatar
tab es6  
fat committed
211
212
213
214
215
216
217
218
219
220
221
222
      }

      if (callback) {
        callback()
      }
    }


    // static

    static _jQueryInterface(config) {
      return this.each(function () {
223
224
        const $this = $(this)
        let data    = $this.data(DATA_KEY)
fat's avatar
tab es6  
fat committed
225
226

        if (!data) {
227
          data = new Tab(this)
fat's avatar
tab es6  
fat committed
228
229
230
231
          $this.data(DATA_KEY, data)
        }

        if (typeof config === 'string') {
232
233
234
          if (data[config] === undefined) {
            throw new Error(`No method named "${config}"`)
          }
fat's avatar
tab es6  
fat committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
          data[config]()
        }
      })
    }

  }


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

  $(document)
fat's avatar
fat committed
250
    .on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
XhmikosR's avatar
XhmikosR committed
251
252
253
      event.preventDefault()
      Tab._jQueryInterface.call($(this), 'show')
    })
fat's avatar
tab es6  
fat committed
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273


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

  $.fn[NAME]             = Tab._jQueryInterface
  $.fn[NAME].Constructor = Tab
  $.fn[NAME].noConflict  = function () {
    $.fn[NAME] = JQUERY_NO_CONFLICT
    return Tab._jQueryInterface
  }

  return Tab

})(jQuery)

export default Tab