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
    }

132
    const isActive = this._element.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

153
    const showEvent = EventHandler.trigger(this._element, 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
    Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
202
    Manipulator.toggleClass(this._element, CLASS_NAME_SHOW)
XhmikosR's avatar
XhmikosR committed
203
    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
    Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
227
    Manipulator.toggleClass(this._element, CLASS_NAME_SHOW)
XhmikosR's avatar
XhmikosR committed
228
    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
    return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
Johann-S's avatar
Johann-S committed
277
  }
fat's avatar
fat committed
278

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

Johann-S's avatar
Johann-S committed
283
    // Handle dropup
XhmikosR's avatar
XhmikosR committed
284
285
286
287
    if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
      placement = PLACEMENT_TOP
      if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) {
        placement = PLACEMENT_TOPEND
288
      }
XhmikosR's avatar
XhmikosR committed
289
290
291
292
293
294
    } 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
295
    }
XhmikosR's avatar
XhmikosR committed
296

Johann-S's avatar
Johann-S committed
297
298
    return placement
  }
299

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

304
305
306
  _getOffset() {
    const offset = {}

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

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

320
321
322
323
    return offset
  }

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

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

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

Johann-S's avatar
Johann-S committed
350
  // Static
fat's avatar
fat committed
351

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

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

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

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

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

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

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

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

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

Johann-S's avatar
Johann-S committed
397
      const dropdownMenu = context._menu
398
      if (!toggles[i].classList.contains(CLASS_NAME_SHOW)) {
Johann-S's avatar
Johann-S committed
399
400
        continue
      }
fat's avatar
fat committed
401

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

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

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

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

423
424
425
426
      if (context._popper) {
        context._popper.destroy()
      }

XhmikosR's avatar
XhmikosR committed
427
      dropdownMenu.classList.remove(CLASS_NAME_SHOW)
428
      toggles[i].classList.remove(CLASS_NAME_SHOW)
XhmikosR's avatar
XhmikosR committed
429
      EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
fat's avatar
fat committed
430
    }
Johann-S's avatar
Johann-S committed
431
  }
fat's avatar
fat committed
432

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

437
  static dataApiKeydownHandler(event) {
Johann-S's avatar
Johann-S committed
438
439
440
441
442
443
444
    // 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
445
    if (/input|textarea/i.test(event.target.tagName) ?
446
447
      event.which === SPACE_KEYCODE || (event.which !== ESCAPE_KEYCODE &&
      ((event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE) ||
XhmikosR's avatar
XhmikosR committed
448
        SelectorEngine.closest(event.target, SELECTOR_MENU))) :
XhmikosR's avatar
XhmikosR committed
449
      !REGEXP_KEYDOWN.test(event.which)) {
Johann-S's avatar
Johann-S committed
450
451
      return
    }
fat's avatar
fat committed
452

Johann-S's avatar
Johann-S committed
453
454
    event.preventDefault()
    event.stopPropagation()
fat's avatar
fat committed
455

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

460
    const parent = Dropdown.getParentFromElement(this)
461
    const isActive = this.classList.contains(CLASS_NAME_SHOW)
fat's avatar
fat committed
462

463
464
465
466
467
468
    if (event.which === ESCAPE_KEYCODE) {
      const button = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
      button.focus()
      Dropdown.clearMenus()
      return
    }
fat's avatar
fat committed
469

470
    if (!isActive || event.which === SPACE_KEYCODE) {
471
      Dropdown.clearMenus()
Johann-S's avatar
Johann-S committed
472
473
      return
    }
fat's avatar
fat committed
474

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

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

482
    let index = items.indexOf(event.target) || 0
Jacob Thornton's avatar
Jacob Thornton committed
483

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

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

Johann-S's avatar
Johann-S committed
492
    items[index].focus()
fat's avatar
fat committed
493
  }
494

495
  static getInstance(element) {
496
497
    return Data.getData(element, DATA_KEY)
  }
Johann-S's avatar
Johann-S committed
498
}
fat's avatar
fat committed
499

Johann-S's avatar
Johann-S committed
500
501
502
503
504
/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
fat's avatar
fat committed
505

XhmikosR's avatar
XhmikosR committed
506
507
508
509
510
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
511
512
  event.preventDefault()
  event.stopPropagation()
513
  Dropdown.dropdownInterface(this, 'toggle')
Johann-S's avatar
Johann-S committed
514
515
})
EventHandler
XhmikosR's avatar
XhmikosR committed
516
  .on(document, EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => e.stopPropagation())
Johann-S's avatar
Johann-S committed
517

518
519
const $ = getjQuery()

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

export default Dropdown