dropdown.js 14.9 KB
Newer Older
fat's avatar
fat committed
1
2
/**
 * --------------------------------------------------------------------------
XhmikosR's avatar
XhmikosR committed
3
 * Bootstrap (v4.3.1): dropdown.js
fat's avatar
fat committed
4
5
6
7
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

8
import {
9
  getjQuery,
10
  getElementFromSelector,
11
  isElement,
12
  isVisible,
13
14
15
  makeArray,
  noop,
  typeCheckConfig
16
17
18
19
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
20
import Popper from 'popper.js'
21
import SelectorEngine from './dom/selector-engine'
22

Johann-S's avatar
Johann-S committed
23
24
25
26
27
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
fat's avatar
fat committed
28

XhmikosR's avatar
XhmikosR committed
29
30
31
32
33
const NAME = 'dropdown'
const VERSION = '4.3.1'
const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
XhmikosR's avatar
XhmikosR committed
34

XhmikosR's avatar
XhmikosR committed
35
36
37
38
39
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
Johann-S's avatar
Johann-S committed
40
41
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)

XhmikosR's avatar
XhmikosR committed
42
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
Johann-S's avatar
Johann-S committed
43

XhmikosR's avatar
XhmikosR committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_CLICK = `click${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`

const CLASS_NAME_DISABLED = 'disabled'
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
const CLASS_NAME_DROPRIGHT = 'dropright'
const CLASS_NAME_DROPLEFT = 'dropleft'
const CLASS_NAME_MENURIGHT = 'dropdown-menu-right'
const CLASS_NAME_NAVBAR = 'navbar'
const CLASS_NAME_POSITION_STATIC = 'position-static'

const SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]'
const SELECTOR_FORM_CHILD = '.dropdown form'
const SELECTOR_MENU = '.dropdown-menu'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'

const PLACEMENT_TOP = 'top-start'
const PLACEMENT_TOPEND = 'top-end'
const PLACEMENT_BOTTOM = 'bottom-start'
const PLACEMENT_BOTTOMEND = 'bottom-end'
const PLACEMENT_RIGHT = 'right-start'
const PLACEMENT_LEFT = 'left-start'
Johann-S's avatar
Johann-S committed
74
75

const Default = {
XhmikosR's avatar
XhmikosR committed
76
77
78
79
  offset: 0,
  flip: true,
  boundary: 'scrollParent',
  reference: 'toggle',
80
81
  display: 'dynamic',
  popperConfig: null
Johann-S's avatar
Johann-S committed
82
83
84
}

const DefaultType = {
XhmikosR's avatar
XhmikosR committed
85
86
87
88
  offset: '(number|string|function)',
  flip: 'boolean',
  boundary: '(string|element)',
  reference: '(string|element)',
89
90
  display: 'string',
  popperConfig: '(null|object)'
Johann-S's avatar
Johann-S committed
91
92
93
94
95
96
97
}

/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
fat's avatar
fat committed
98

Johann-S's avatar
Johann-S committed
99
100
class Dropdown {
  constructor(element, config) {
XhmikosR's avatar
XhmikosR committed
101
102
103
104
    this._element = element
    this._popper = null
    this._config = this._getConfig(config)
    this._menu = this._getMenuElement()
Johann-S's avatar
Johann-S committed
105
106
107
    this._inNavbar = this._detectNavbar()

    this._addEventListeners()
108
    Data.setData(element, DATA_KEY, this)
fat's avatar
fat committed
109
110
  }

Johann-S's avatar
Johann-S committed
111
112
113
114
  // Getters

  static get VERSION() {
    return VERSION
Johann-S's avatar
Johann-S committed
115
116
  }

Johann-S's avatar
Johann-S committed
117
118
  static get Default() {
    return Default
Johann-S's avatar
Johann-S committed
119
120
  }

Johann-S's avatar
Johann-S committed
121
122
  static get DefaultType() {
    return DefaultType
Johann-S's avatar
Johann-S committed
123
124
  }

Johann-S's avatar
Johann-S committed
125
126
127
  // Public

  toggle() {
XhmikosR's avatar
XhmikosR committed
128
    if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED)) {
Johann-S's avatar
Johann-S committed
129
      return
fat's avatar
fat committed
130
131
    }

XhmikosR's avatar
XhmikosR committed
132
    const isActive = this._menu.classList.contains(CLASS_NAME_SHOW)
133

134
    Dropdown.clearMenus()
135

Johann-S's avatar
Johann-S committed
136
137
    if (isActive) {
      return
Johann-S's avatar
Johann-S committed
138
139
    }

140
141
142
143
    this.show()
  }

  show() {
XhmikosR's avatar
XhmikosR committed
144
    if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED) || this._menu.classList.contains(CLASS_NAME_SHOW)) {
145
146
147
148
      return
    }

    const parent = Dropdown.getParentFromElement(this._element)
Johann-S's avatar
Johann-S committed
149
150
    const relatedTarget = {
      relatedTarget: this._element
Johann-S's avatar
Johann-S committed
151
    }
152

XhmikosR's avatar
XhmikosR committed
153
    const showEvent = EventHandler.trigger(parent, EVENT_SHOW, relatedTarget)
Johann-S's avatar
Johann-S committed
154

Johann-S's avatar
Johann-S committed
155
    if (showEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
156
157
      return
    }
fat's avatar
fat committed
158

Johann-S's avatar
Johann-S committed
159
160
161
    // Disable totally Popper.js for Dropdown in Navbar
    if (!this._inNavbar) {
      if (typeof Popper === 'undefined') {
162
        throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org)')
fat's avatar
fat committed
163
164
      }

Johann-S's avatar
Johann-S committed
165
      let referenceElement = this._element
fat's avatar
fat committed
166

Johann-S's avatar
Johann-S committed
167
168
      if (this._config.reference === 'parent') {
        referenceElement = parent
169
      } else if (isElement(this._config.reference)) {
Johann-S's avatar
Johann-S committed
170
        referenceElement = this._config.reference
fat's avatar
fat committed
171

Johann-S's avatar
Johann-S committed
172
173
174
        // Check if it's jQuery element
        if (typeof this._config.reference.jquery !== 'undefined') {
          referenceElement = this._config.reference[0]
175
        }
176
      }
177

Johann-S's avatar
Johann-S committed
178
179
180
181
      // If boundary is not `scrollParent`, then set position to `static`
      // to allow the menu to "escape" the scroll parent's boundaries
      // https://github.com/twbs/bootstrap/issues/24251
      if (this._config.boundary !== 'scrollParent') {
XhmikosR's avatar
XhmikosR committed
182
        parent.classList.add(CLASS_NAME_POSITION_STATIC)
183
      }
XhmikosR's avatar
XhmikosR committed
184

Johann-S's avatar
Johann-S committed
185
      this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
fat's avatar
fat committed
186
187
    }

Johann-S's avatar
Johann-S committed
188
189
190
191
192
    // If this is a touch-enabled device we add extra
    // empty mouseover listeners to the body's immediate children;
    // only needed because of broken event delegation on iOS
    // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
    if ('ontouchstart' in document.documentElement &&
XhmikosR's avatar
XhmikosR committed
193
      !makeArray(SelectorEngine.closest(parent, SELECTOR_NAVBAR_NAV)).length) {
194
      makeArray(document.body.children)
XhmikosR's avatar
XhmikosR committed
195
        .forEach(elem => EventHandler.on(elem, 'mouseover', null, noop()))
fat's avatar
fat committed
196
197
    }

Johann-S's avatar
Johann-S committed
198
199
    this._element.focus()
    this._element.setAttribute('aria-expanded', true)
fat's avatar
fat committed
200

XhmikosR's avatar
XhmikosR committed
201
202
203
    Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
    Manipulator.toggleClass(parent, CLASS_NAME_SHOW)
    EventHandler.trigger(parent, EVENT_SHOWN, relatedTarget)
Johann-S's avatar
Johann-S committed
204
  }
fat's avatar
fat committed
205

206
  hide() {
XhmikosR's avatar
XhmikosR committed
207
    if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED) || !this._menu.classList.contains(CLASS_NAME_SHOW)) {
208
209
210
      return
    }

211
    const parent = Dropdown.getParentFromElement(this._element)
212
213
214
    const relatedTarget = {
      relatedTarget: this._element
    }
215

XhmikosR's avatar
XhmikosR committed
216
    const hideEvent = EventHandler.trigger(parent, EVENT_HIDE, relatedTarget)
217

Johann-S's avatar
Johann-S committed
218
    if (hideEvent.defaultPrevented) {
219
220
221
      return
    }

222
223
224
225
    if (this._popper) {
      this._popper.destroy()
    }

XhmikosR's avatar
XhmikosR committed
226
227
228
    Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
    Manipulator.toggleClass(parent, CLASS_NAME_SHOW)
    EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
229
230
  }

Johann-S's avatar
Johann-S committed
231
  dispose() {
Johann-S's avatar
Johann-S committed
232
233
    Data.removeData(this._element, DATA_KEY)
    EventHandler.off(this._element, EVENT_KEY)
Johann-S's avatar
Johann-S committed
234
235
    this._element = null
    this._menu = null
236
    if (this._popper) {
Johann-S's avatar
Johann-S committed
237
238
      this._popper.destroy()
      this._popper = null
fat's avatar
fat committed
239
    }
Johann-S's avatar
Johann-S committed
240
  }
fat's avatar
fat committed
241

Johann-S's avatar
Johann-S committed
242
243
  update() {
    this._inNavbar = this._detectNavbar()
244
    if (this._popper) {
Johann-S's avatar
Johann-S committed
245
246
247
      this._popper.scheduleUpdate()
    }
  }
Johann-S's avatar
Johann-S committed
248

Johann-S's avatar
Johann-S committed
249
250
251
  // Private

  _addEventListeners() {
XhmikosR's avatar
XhmikosR committed
252
    EventHandler.on(this._element, EVENT_CLICK, event => {
Johann-S's avatar
Johann-S committed
253
254
255
256
257
      event.preventDefault()
      event.stopPropagation()
      this.toggle()
    })
  }
Johann-S's avatar
Johann-S committed
258

Johann-S's avatar
Johann-S committed
259
260
261
  _getConfig(config) {
    config = {
      ...this.constructor.Default,
262
      ...Manipulator.getDataAttributes(this._element),
Johann-S's avatar
Johann-S committed
263
      ...config
Johann-S's avatar
Johann-S committed
264
265
    }

266
    typeCheckConfig(
Johann-S's avatar
Johann-S committed
267
268
269
270
271
272
273
274
275
      NAME,
      config,
      this.constructor.DefaultType
    )

    return config
  }

  _getMenuElement() {
276
    const parent = Dropdown.getParentFromElement(this._element)
XhmikosR's avatar
XhmikosR committed
277

XhmikosR's avatar
XhmikosR committed
278
    return SelectorEngine.findOne(SELECTOR_MENU, parent)
Johann-S's avatar
Johann-S committed
279
  }
fat's avatar
fat committed
280

Johann-S's avatar
Johann-S committed
281
  _getPlacement() {
Johann-S's avatar
Johann-S committed
282
    const parentDropdown = this._element.parentNode
XhmikosR's avatar
XhmikosR committed
283
    let placement = PLACEMENT_BOTTOM
284

Johann-S's avatar
Johann-S committed
285
    // Handle dropup
XhmikosR's avatar
XhmikosR committed
286
287
288
289
    if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
      placement = PLACEMENT_TOP
      if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) {
        placement = PLACEMENT_TOPEND
290
      }
XhmikosR's avatar
XhmikosR committed
291
292
293
294
295
296
    } else if (parentDropdown.classList.contains(CLASS_NAME_DROPRIGHT)) {
      placement = PLACEMENT_RIGHT
    } else if (parentDropdown.classList.contains(CLASS_NAME_DROPLEFT)) {
      placement = PLACEMENT_LEFT
    } else if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) {
      placement = PLACEMENT_BOTTOMEND
297
    }
XhmikosR's avatar
XhmikosR committed
298

Johann-S's avatar
Johann-S committed
299
300
    return placement
  }
301

Johann-S's avatar
Johann-S committed
302
  _detectNavbar() {
XhmikosR's avatar
XhmikosR committed
303
    return Boolean(SelectorEngine.closest(this._element, `.${CLASS_NAME_NAVBAR}`))
Johann-S's avatar
Johann-S committed
304
  }
305

306
307
308
  _getOffset() {
    const offset = {}

Johann-S's avatar
Johann-S committed
309
    if (typeof this._config.offset === 'function') {
XhmikosR's avatar
XhmikosR committed
310
      offset.fn = data => {
Johann-S's avatar
Johann-S committed
311
312
        data.offsets = {
          ...data.offsets,
313
          ...this._config.offset(data.offsets, this._element) || {}
314
        }
315

Johann-S's avatar
Johann-S committed
316
        return data
317
      }
Johann-S's avatar
Johann-S committed
318
    } else {
319
      offset.offset = this._config.offset
Johann-S's avatar
Johann-S committed
320
    }
321

322
323
324
325
    return offset
  }

  _getPopperConfig() {
326
    const popperConfig = {
Johann-S's avatar
Johann-S committed
327
328
      placement: this._getPlacement(),
      modifiers: {
329
        offset: this._getOffset(),
Johann-S's avatar
Johann-S committed
330
331
332
333
334
        flip: {
          enabled: this._config.flip
        },
        preventOverflow: {
          boundariesElement: this._config.boundary
335
336
        }
      }
Johann-S's avatar
Johann-S committed
337
    }
338

Johann-S's avatar
Johann-S committed
339
340
341
342
    // Disable Popper.js if we have a static display
    if (this._config.display === 'static') {
      popperConfig.modifiers.applyStyle = {
        enabled: false
343
      }
344
    }
345

346
347
348
    return {
      ...popperConfig,
      ...this._config.popperConfig
349
    }
Johann-S's avatar
Johann-S committed
350
  }
351

Johann-S's avatar
Johann-S committed
352
  // Static
fat's avatar
fat committed
353

354
  static dropdownInterface(element, config) {
Johann-S's avatar
Johann-S committed
355
356
    let data = Data.getData(element, DATA_KEY)
    const _config = typeof config === 'object' ? config : null
fat's avatar
fat committed
357

Johann-S's avatar
Johann-S committed
358
359
360
    if (!data) {
      data = new Dropdown(element, _config)
    }
fat's avatar
fat committed
361

Johann-S's avatar
Johann-S committed
362
363
    if (typeof config === 'string') {
      if (typeof data[config] === 'undefined') {
XhmikosR's avatar
XhmikosR committed
364
        throw new TypeError(`No method named "${config}"`)
Johann-S's avatar
Johann-S committed
365
      }
XhmikosR's avatar
XhmikosR committed
366

Johann-S's avatar
Johann-S committed
367
368
369
370
      data[config]()
    }
  }

371
  static jQueryInterface(config) {
Johann-S's avatar
Johann-S committed
372
    return this.each(function () {
373
      Dropdown.dropdownInterface(this, config)
Johann-S's avatar
Johann-S committed
374
375
376
    })
  }

377
  static clearMenus(event) {
Johann-S's avatar
Johann-S committed
378
    if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
379
      (event.type === 'keyup' && event.which !== TAB_KEYCODE))) {
Johann-S's avatar
Johann-S committed
380
      return
fat's avatar
fat committed
381
382
    }

XhmikosR's avatar
XhmikosR committed
383
    const toggles = makeArray(SelectorEngine.find(SELECTOR_DATA_TOGGLE))
Johann-S's avatar
Johann-S committed
384
    for (let i = 0, len = toggles.length; i < len; i++) {
385
      const parent = Dropdown.getParentFromElement(toggles[i])
XhmikosR's avatar
XhmikosR committed
386
      const context = Data.getData(toggles[i], DATA_KEY)
Johann-S's avatar
Johann-S committed
387
388
      const relatedTarget = {
        relatedTarget: toggles[i]
fat's avatar
fat committed
389
390
      }

Johann-S's avatar
Johann-S committed
391
392
393
      if (event && event.type === 'click') {
        relatedTarget.clickEvent = event
      }
394

Johann-S's avatar
Johann-S committed
395
396
397
      if (!context) {
        continue
      }
Johann-S's avatar
Johann-S committed
398

Johann-S's avatar
Johann-S committed
399
      const dropdownMenu = context._menu
XhmikosR's avatar
XhmikosR committed
400
      if (!parent.classList.contains(CLASS_NAME_SHOW)) {
Johann-S's avatar
Johann-S committed
401
402
        continue
      }
fat's avatar
fat committed
403

404
405
406
      if (event && ((event.type === 'click' &&
          /input|textarea/i.test(event.target.tagName)) ||
          (event.type === 'keyup' && event.which === TAB_KEYCODE)) &&
Johann-S's avatar
Johann-S committed
407
          parent.contains(event.target)) {
Johann-S's avatar
Johann-S committed
408
409
        continue
      }
fat's avatar
fat committed
410

XhmikosR's avatar
XhmikosR committed
411
      const hideEvent = EventHandler.trigger(parent, EVENT_HIDE, relatedTarget)
Johann-S's avatar
Johann-S committed
412
      if (hideEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
413
414
        continue
      }
fat's avatar
fat committed
415

Johann-S's avatar
Johann-S committed
416
417
418
      // If this is a touch-enabled device we remove the extra
      // empty mouseover listeners we added for iOS support
      if ('ontouchstart' in document.documentElement) {
419
        makeArray(document.body.children)
XhmikosR's avatar
XhmikosR committed
420
          .forEach(elem => EventHandler.off(elem, 'mouseover', null, noop()))
Johann-S's avatar
Johann-S committed
421
      }
422

Johann-S's avatar
Johann-S committed
423
      toggles[i].setAttribute('aria-expanded', 'false')
fat's avatar
fat committed
424

425
426
427
428
      if (context._popper) {
        context._popper.destroy()
      }

XhmikosR's avatar
XhmikosR committed
429
430
431
      dropdownMenu.classList.remove(CLASS_NAME_SHOW)
      parent.classList.remove(CLASS_NAME_SHOW)
      EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
fat's avatar
fat committed
432
    }
Johann-S's avatar
Johann-S committed
433
  }
fat's avatar
fat committed
434

435
  static getParentFromElement(element) {
436
    return getElementFromSelector(element) || element.parentNode
Johann-S's avatar
Johann-S committed
437
  }
fat's avatar
fat committed
438

439
  static dataApiKeydownHandler(event) {
Johann-S's avatar
Johann-S committed
440
441
442
443
444
445
446
    // If not input/textarea:
    //  - And not a key in REGEXP_KEYDOWN => not a dropdown command
    // If input/textarea:
    //  - If space key => not a dropdown command
    //  - If key is other than escape
    //    - If key is not up or down => not a dropdown command
    //    - If trigger inside the menu => not a dropdown command
XhmikosR's avatar
XhmikosR committed
447
    if (/input|textarea/i.test(event.target.tagName) ?
448
449
      event.which === SPACE_KEYCODE || (event.which !== ESCAPE_KEYCODE &&
      ((event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE) ||
XhmikosR's avatar
XhmikosR committed
450
        SelectorEngine.closest(event.target, SELECTOR_MENU))) :
XhmikosR's avatar
XhmikosR committed
451
      !REGEXP_KEYDOWN.test(event.which)) {
Johann-S's avatar
Johann-S committed
452
453
      return
    }
fat's avatar
fat committed
454

Johann-S's avatar
Johann-S committed
455
456
    event.preventDefault()
    event.stopPropagation()
fat's avatar
fat committed
457

XhmikosR's avatar
XhmikosR committed
458
    if (this.disabled || this.classList.contains(CLASS_NAME_DISABLED)) {
Johann-S's avatar
Johann-S committed
459
460
      return
    }
fat's avatar
fat committed
461

462
    const parent = Dropdown.getParentFromElement(this)
XhmikosR's avatar
XhmikosR committed
463
    const isActive = parent.classList.contains(CLASS_NAME_SHOW)
fat's avatar
fat committed
464

465
    if (!isActive || (isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE))) {
Johann-S's avatar
Johann-S committed
466
      if (event.which === ESCAPE_KEYCODE) {
XhmikosR's avatar
XhmikosR committed
467
        SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, parent).focus()
fat's avatar
fat committed
468
469
      }

470
      Dropdown.clearMenus()
Johann-S's avatar
Johann-S committed
471
472
      return
    }
fat's avatar
fat committed
473

XhmikosR's avatar
XhmikosR committed
474
    const items = makeArray(SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, parent))
475
      .filter(isVisible)
fat's avatar
fat committed
476

Johann-S's avatar
Johann-S committed
477
    if (!items.length) {
Johann-S's avatar
Johann-S committed
478
479
      return
    }
fat's avatar
fat committed
480

Johann-S's avatar
Johann-S committed
481
    let index = items.indexOf(event.target)
Jacob Thornton's avatar
Jacob Thornton committed
482

Johann-S's avatar
Johann-S committed
483
484
485
    if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
      index--
    }
Jacob Thornton's avatar
Jacob Thornton committed
486

Johann-S's avatar
Johann-S committed
487
488
489
    if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down
      index++
    }
fat's avatar
fat committed
490

Johann-S's avatar
Johann-S committed
491
492
    if (index < 0) {
      index = 0
fat's avatar
fat committed
493
    }
Johann-S's avatar
Johann-S committed
494
495

    items[index].focus()
fat's avatar
fat committed
496
  }
497

498
  static getInstance(element) {
499
500
    return Data.getData(element, DATA_KEY)
  }
Johann-S's avatar
Johann-S committed
501
}
fat's avatar
fat committed
502

Johann-S's avatar
Johann-S committed
503
504
505
506
507
/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
fat's avatar
fat committed
508

XhmikosR's avatar
XhmikosR committed
509
510
511
512
513
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
Johann-S's avatar
Johann-S committed
514
515
  event.preventDefault()
  event.stopPropagation()
516
  Dropdown.dropdownInterface(this, 'toggle')
Johann-S's avatar
Johann-S committed
517
518
})
EventHandler
XhmikosR's avatar
XhmikosR committed
519
  .on(document, EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => e.stopPropagation())
Johann-S's avatar
Johann-S committed
520

521
522
const $ = getjQuery()

Johann-S's avatar
Johann-S committed
523
524
525
526
/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
Johann-S's avatar
Johann-S committed
527
 * add .dropdown to jQuery only if jQuery is present
Johann-S's avatar
Johann-S committed
528
 */
Johann-S's avatar
Johann-S committed
529
/* istanbul ignore if */
530
if ($) {
Johann-S's avatar
Johann-S committed
531
  const JQUERY_NO_CONFLICT = $.fn[NAME]
532
  $.fn[NAME] = Dropdown.jQueryInterface
XhmikosR's avatar
XhmikosR committed
533
534
  $.fn[NAME].Constructor = Dropdown
  $.fn[NAME].noConflict = () => {
Johann-S's avatar
Johann-S committed
535
    $.fn[NAME] = JQUERY_NO_CONFLICT
536
    return Dropdown.jQueryInterface
Johann-S's avatar
Johann-S committed
537
  }
Johann-S's avatar
Johann-S committed
538
}
fat's avatar
fat committed
539
540

export default Dropdown