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

8
9
10
11
12
13
14
15
16
17
18
import {
  jQuery as $,
  TRANSITION_END,
  emulateTransitionEnd,
  getSelectorFromElement,
  getTransitionDurationFromElement,
  isVisible,
  makeArray,
  reflow,
  typeCheckConfig
} from './util/index'
19
20
21
22
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selectorEngine'
23

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

Johann-S's avatar
Johann-S committed
30
const NAME               = 'modal'
XhmikosR's avatar
XhmikosR committed
31
const VERSION            = '4.3.1'
Johann-S's avatar
Johann-S committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
const DATA_KEY           = 'bs.modal'
const EVENT_KEY          = `.${DATA_KEY}`
const DATA_API_KEY       = '.data-api'
const ESCAPE_KEYCODE     = 27 // KeyboardEvent.which value for Escape (Esc) key

const Default = {
  backdrop : true,
  keyboard : true,
  focus    : true,
  show     : true
}

const DefaultType = {
  backdrop : '(boolean|string)',
  keyboard : 'boolean',
  focus    : 'boolean',
  show     : 'boolean'
}

const Event = {
  HIDE              : `hide${EVENT_KEY}`,
  HIDDEN            : `hidden${EVENT_KEY}`,
  SHOW              : `show${EVENT_KEY}`,
  SHOWN             : `shown${EVENT_KEY}`,
  FOCUSIN           : `focusin${EVENT_KEY}`,
  RESIZE            : `resize${EVENT_KEY}`,
  CLICK_DISMISS     : `click.dismiss${EVENT_KEY}`,
  KEYDOWN_DISMISS   : `keydown.dismiss${EVENT_KEY}`,
  MOUSEUP_DISMISS   : `mouseup.dismiss${EVENT_KEY}`,
  MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`,
  CLICK_DATA_API    : `click${EVENT_KEY}${DATA_API_KEY}`
}

const ClassName = {
Shohei Yoshida's avatar
Shohei Yoshida committed
66
  SCROLLABLE         : 'modal-dialog-scrollable',
Johann-S's avatar
Johann-S committed
67
68
69
70
71
72
73
74
  SCROLLBAR_MEASURER : 'modal-scrollbar-measure',
  BACKDROP           : 'modal-backdrop',
  OPEN               : 'modal-open',
  FADE               : 'fade',
  SHOW               : 'show'
}

const Selector = {
75
  DIALOG         : '.modal-dialog',
Shohei Yoshida's avatar
Shohei Yoshida committed
76
  MODAL_BODY     : '.modal-body',
77
78
79
80
  DATA_TOGGLE    : '[data-toggle="modal"]',
  DATA_DISMISS   : '[data-dismiss="modal"]',
  FIXED_CONTENT  : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
  STICKY_CONTENT : '.sticky-top'
Johann-S's avatar
Johann-S committed
81
}
fat's avatar
fat committed
82

Johann-S's avatar
Johann-S committed
83
84
85
86
87
/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
88

Johann-S's avatar
Johann-S committed
89
90
91
92
class Modal {
  constructor(element, config) {
    this._config              = this._getConfig(config)
    this._element             = element
93
    this._dialog              = SelectorEngine.findOne(Selector.DIALOG, element)
Johann-S's avatar
Johann-S committed
94
95
96
97
    this._backdrop            = null
    this._isShown             = false
    this._isBodyOverflowing   = false
    this._ignoreBackdropClick = false
98
    this._isTransitioning     = false
Johann-S's avatar
Johann-S committed
99
    this._scrollbarWidth      = 0
100
    Data.setData(element, DATA_KEY, this)
101
102
  }

Johann-S's avatar
Johann-S committed
103
104
105
106
  // Getters

  static get VERSION() {
    return VERSION
107
108
  }

Johann-S's avatar
Johann-S committed
109
110
111
  static get Default() {
    return Default
  }
112

Johann-S's avatar
Johann-S committed
113
  // Public
114

Johann-S's avatar
Johann-S committed
115
116
117
  toggle(relatedTarget) {
    return this._isShown ? this.hide() : this.show(relatedTarget)
  }
118

Johann-S's avatar
Johann-S committed
119
  show(relatedTarget) {
120
    if (this._isShown || this._isTransitioning) {
Johann-S's avatar
Johann-S committed
121
      return
122
123
    }

124
    if (this._element.classList.contains(ClassName.FADE)) {
Johann-S's avatar
Johann-S committed
125
      this._isTransitioning = true
126
127
    }

128
    const showEvent = EventHandler.trigger(this._element, Event.SHOW, {
Johann-S's avatar
Johann-S committed
129
130
      relatedTarget
    })
131

Johann-S's avatar
Johann-S committed
132
    if (this._isShown || showEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
133
134
      return
    }
135

Johann-S's avatar
Johann-S committed
136
    this._isShown = true
137

Johann-S's avatar
Johann-S committed
138
139
    this._checkScrollbar()
    this._setScrollbar()
140

Johann-S's avatar
Johann-S committed
141
    this._adjustDialog()
142

Johann-S's avatar
Johann-S committed
143
144
    this._setEscapeEvent()
    this._setResizeEvent()
David Bailey's avatar
David Bailey committed
145

146
    EventHandler.on(this._element,
Johann-S's avatar
Johann-S committed
147
148
149
150
      Event.CLICK_DISMISS,
      Selector.DATA_DISMISS,
      (event) => this.hide(event)
    )
151

152
153
154
    EventHandler.on(this._dialog, Event.MOUSEDOWN_DISMISS, () => {
      EventHandler.one(this._element, Event.MOUSEUP_DISMISS, (event) => {
        if (event.target === this._element) {
Johann-S's avatar
Johann-S committed
155
156
157
158
          this._ignoreBackdropClick = true
        }
      })
    })
159

Johann-S's avatar
Johann-S committed
160
161
    this._showBackdrop(() => this._showElement(relatedTarget))
  }
162

Johann-S's avatar
Johann-S committed
163
164
165
166
  hide(event) {
    if (event) {
      event.preventDefault()
    }
167

168
    if (!this._isShown || this._isTransitioning) {
Johann-S's avatar
Johann-S committed
169
      return
170
171
    }

172
    const hideEvent = EventHandler.trigger(this._element, Event.HIDE)
173

Johann-S's avatar
Johann-S committed
174
    if (!this._isShown || hideEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
175
176
      return
    }
177

Johann-S's avatar
Johann-S committed
178
    this._isShown = false
179
    const transition = this._element.classList.contains(ClassName.FADE)
180

Johann-S's avatar
Johann-S committed
181
182
183
    if (transition) {
      this._isTransitioning = true
    }
184

Johann-S's avatar
Johann-S committed
185
186
    this._setEscapeEvent()
    this._setResizeEvent()
187

188
    EventHandler.off(document, Event.FOCUSIN)
189

190
    this._element.classList.remove(ClassName.SHOW)
191

192
193
    EventHandler.off(this._element, Event.CLICK_DISMISS)
    EventHandler.off(this._dialog, Event.MOUSEDOWN_DISMISS)
194
195


Johann-S's avatar
Johann-S committed
196
    if (transition) {
197
      const transitionDuration = getTransitionDurationFromElement(this._element)
198

199
200
      EventHandler.one(this._element, TRANSITION_END, (event) => this._hideModal(event))
      emulateTransitionEnd(this._element, transitionDuration)
Johann-S's avatar
Johann-S committed
201
202
203
204
    } else {
      this._hideModal()
    }
  }
205

Johann-S's avatar
Johann-S committed
206
  dispose() {
207
    [window, this._element, this._dialog]
208
      .forEach((htmlElement) => EventHandler.off(htmlElement, EVENT_KEY))
209
210
211
212
213
214

    /**
     * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`
     * Do not move `document` in `htmlElements` array
     * It will remove `Event.CLICK_DATA_API` event that should remain
     */
215
    EventHandler.off(document, Event.FOCUSIN)
216

217
    Data.removeData(this._element, DATA_KEY)
218

Johann-S's avatar
Johann-S committed
219
220
221
222
223
224
225
    this._config              = null
    this._element             = null
    this._dialog              = null
    this._backdrop            = null
    this._isShown             = null
    this._isBodyOverflowing   = null
    this._ignoreBackdropClick = null
226
    this._isTransitioning     = null
Johann-S's avatar
Johann-S committed
227
228
    this._scrollbarWidth      = null
  }
fat's avatar
fat committed
229

Johann-S's avatar
Johann-S committed
230
231
232
  handleUpdate() {
    this._adjustDialog()
  }
fat's avatar
fat committed
233

Johann-S's avatar
Johann-S committed
234
  // Private
fat's avatar
fat committed
235

Johann-S's avatar
Johann-S committed
236
237
238
239
  _getConfig(config) {
    config = {
      ...Default,
      ...config
240
    }
241
    typeCheckConfig(NAME, config, DefaultType)
Johann-S's avatar
Johann-S committed
242
243
    return config
  }
244

Johann-S's avatar
Johann-S committed
245
  _showElement(relatedTarget) {
246
    const transition = this._element.classList.contains(ClassName.FADE)
247

Johann-S's avatar
Johann-S committed
248
249
250
251
    if (!this._element.parentNode ||
        this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
      // Don't move modal's DOM position
      document.body.appendChild(this._element)
fat's avatar
fat committed
252
253
    }

Johann-S's avatar
Johann-S committed
254
255
    this._element.style.display = 'block'
    this._element.removeAttribute('aria-hidden')
256
    this._element.setAttribute('aria-modal', true)
Shohei Yoshida's avatar
Shohei Yoshida committed
257

258
    if (this._dialog.classList.contains(ClassName.SCROLLABLE)) {
259
      SelectorEngine.findOne(Selector.MODAL_BODY, this._dialog).scrollTop = 0
Shohei Yoshida's avatar
Shohei Yoshida committed
260
261
262
    } else {
      this._element.scrollTop = 0
    }
263

Johann-S's avatar
Johann-S committed
264
    if (transition) {
265
      reflow(this._element)
Johann-S's avatar
Johann-S committed
266
    }
267

268
    this._element.classList.add(ClassName.SHOW)
269

Johann-S's avatar
Johann-S committed
270
271
272
    if (this._config.focus) {
      this._enforceFocus()
    }
273

Johann-S's avatar
Johann-S committed
274
    const transitionComplete = () => {
Jacob Thornton's avatar
Jacob Thornton committed
275
      if (this._config.focus) {
Johann-S's avatar
Johann-S committed
276
        this._element.focus()
Jacob Thornton's avatar
Jacob Thornton committed
277
      }
Johann-S's avatar
Johann-S committed
278
      this._isTransitioning = false
279
280
281
      EventHandler.trigger(this._element, Event.SHOWN, {
        relatedTarget
      })
Johann-S's avatar
Johann-S committed
282
    }
283

Johann-S's avatar
Johann-S committed
284
    if (transition) {
285
      const transitionDuration  = getTransitionDurationFromElement(this._dialog)
286

287
288
      EventHandler.one(this._dialog, TRANSITION_END, transitionComplete)
      emulateTransitionEnd(this._dialog, transitionDuration)
Johann-S's avatar
Johann-S committed
289
290
291
292
293
294
    } else {
      transitionComplete()
    }
  }

  _enforceFocus() {
Johann-S's avatar
Johann-S committed
295
296
297
298
299
300
301
302
    EventHandler.off(document, Event.FOCUSIN) // guard against infinite focus loop
    EventHandler.on(document, Event.FOCUSIN, (event) => {
      if (document !== event.target &&
          this._element !== event.target &&
          !this._element.contains(event.target)) {
        this._element.focus()
      }
    })
Johann-S's avatar
Johann-S committed
303
  }
304

Johann-S's avatar
Johann-S committed
305
306
  _setEscapeEvent() {
    if (this._isShown && this._config.keyboard) {
307
      EventHandler.on(this._element, Event.KEYDOWN_DISMISS, (event) => {
Johann-S's avatar
Johann-S committed
308
309
310
311
312
313
        if (event.which === ESCAPE_KEYCODE) {
          event.preventDefault()
          this.hide()
        }
      })
    } else if (!this._isShown) {
314
      EventHandler.off(this._element, Event.KEYDOWN_DISMISS)
315
    }
Johann-S's avatar
Johann-S committed
316
  }
317

Johann-S's avatar
Johann-S committed
318
319
  _setResizeEvent() {
    if (this._isShown) {
320
      EventHandler.on(window, Event.RESIZE, (event) => this.handleUpdate(event))
Johann-S's avatar
Johann-S committed
321
    } else {
322
      EventHandler.off(window, Event.RESIZE)
323
    }
Johann-S's avatar
Johann-S committed
324
  }
325

Johann-S's avatar
Johann-S committed
326
327
328
  _hideModal() {
    this._element.style.display = 'none'
    this._element.setAttribute('aria-hidden', true)
329
    this._element.removeAttribute('aria-modal')
Johann-S's avatar
Johann-S committed
330
331
    this._isTransitioning = false
    this._showBackdrop(() => {
332
      document.body.classList.remove(ClassName.OPEN)
Johann-S's avatar
Johann-S committed
333
334
      this._resetAdjustments()
      this._resetScrollbar()
335
      EventHandler.trigger(this._element, Event.HIDDEN)
Johann-S's avatar
Johann-S committed
336
337
338
339
340
    })
  }

  _removeBackdrop() {
    if (this._backdrop) {
341
      this._backdrop.parentNode.removeChild(this._backdrop)
Johann-S's avatar
Johann-S committed
342
      this._backdrop = null
343
    }
Johann-S's avatar
Johann-S committed
344
  }
345

Johann-S's avatar
Johann-S committed
346
  _showBackdrop(callback) {
347
348
349
    const animate = this._element.classList.contains(ClassName.FADE)
      ? ClassName.FADE
      : ''
Johann-S's avatar
Johann-S committed
350
351
352
353
354
355
356

    if (this._isShown && this._config.backdrop) {
      this._backdrop = document.createElement('div')
      this._backdrop.className = ClassName.BACKDROP

      if (animate) {
        this._backdrop.classList.add(animate)
357
358
      }

359
      document.body.appendChild(this._backdrop)
Johann-S's avatar
Johann-S committed
360

361
      EventHandler.on(this._element, Event.CLICK_DISMISS, (event) => {
Johann-S's avatar
Johann-S committed
362
363
364
365
366
367
368
369
370
371
372
373
        if (this._ignoreBackdropClick) {
          this._ignoreBackdropClick = false
          return
        }
        if (event.target !== event.currentTarget) {
          return
        }
        if (this._config.backdrop === 'static') {
          this._element.focus()
        } else {
          this.hide()
        }
374
375
      })

Johann-S's avatar
Johann-S committed
376
      if (animate) {
377
        reflow(this._backdrop)
378
379
      }

380
      this._backdrop.classList.add(ClassName.SHOW)
381

Johann-S's avatar
Johann-S committed
382
383
384
      if (!callback) {
        return
      }
385

Johann-S's avatar
Johann-S committed
386
387
388
389
      if (!animate) {
        callback()
        return
      }
390

391
      const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
392

393
394
      EventHandler.one(this._backdrop, TRANSITION_END, callback)
      emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
Johann-S's avatar
Johann-S committed
395
    } else if (!this._isShown && this._backdrop) {
396
      this._backdrop.classList.remove(ClassName.SHOW)
397

Johann-S's avatar
Johann-S committed
398
399
400
      const callbackRemove = () => {
        this._removeBackdrop()
        if (callback) {
401
402
          callback()
        }
Johann-S's avatar
Johann-S committed
403
      }
404

405
      if (this._element.classList.contains(ClassName.FADE)) {
406
407
408
        const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
        EventHandler.one(this._backdrop, TRANSITION_END, callbackRemove)
        emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
Johann-S's avatar
Johann-S committed
409
410
      } else {
        callbackRemove()
411
      }
Johann-S's avatar
Johann-S committed
412
413
    } else if (callback) {
      callback()
414
    }
Johann-S's avatar
Johann-S committed
415
  }
416

Johann-S's avatar
Johann-S committed
417
418
419
420
  // ----------------------------------------------------------------------
  // the following methods are used to handle overflowing modals
  // todo (fat): these should probably be refactored out of modal.js
  // ----------------------------------------------------------------------
421

Johann-S's avatar
Johann-S committed
422
423
424
  _adjustDialog() {
    const isModalOverflowing =
      this._element.scrollHeight > document.documentElement.clientHeight
425

Johann-S's avatar
Johann-S committed
426
427
    if (!this._isBodyOverflowing && isModalOverflowing) {
      this._element.style.paddingLeft = `${this._scrollbarWidth}px`
428
429
    }

Johann-S's avatar
Johann-S committed
430
431
    if (this._isBodyOverflowing && !isModalOverflowing) {
      this._element.style.paddingRight = `${this._scrollbarWidth}px`
432
    }
Johann-S's avatar
Johann-S committed
433
  }
434

Johann-S's avatar
Johann-S committed
435
436
437
438
  _resetAdjustments() {
    this._element.style.paddingLeft = ''
    this._element.style.paddingRight = ''
  }
439

Johann-S's avatar
Johann-S committed
440
441
442
443
444
  _checkScrollbar() {
    const rect = document.body.getBoundingClientRect()
    this._isBodyOverflowing = rect.left + rect.right < window.innerWidth
    this._scrollbarWidth = this._getScrollbarWidth()
  }
445

Johann-S's avatar
Johann-S committed
446
447
448
449
450
451
  _setScrollbar() {
    if (this._isBodyOverflowing) {
      // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
      //   while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set

      // Adjust fixed content padding
452
      makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
453
454
455
456
457
458
        .forEach((element) => {
          const actualPadding = element.style.paddingRight
          const calculatedPadding = window.getComputedStyle(element)['padding-right']
          Manipulator.setDataAttribute(element, 'padding-right', actualPadding)
          element.style.paddingRight = `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`
        })
459

Johann-S's avatar
Johann-S committed
460
      // Adjust sticky content margin
461
      makeArray(SelectorEngine.find(Selector.STICKY_CONTENT))
462
463
464
465
466
467
        .forEach((element) => {
          const actualMargin = element.style.marginRight
          const calculatedMargin = window.getComputedStyle(element)['margin-right']
          Manipulator.setDataAttribute(element, 'margin-right', actualMargin)
          element.style.marginRight = `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`
        })
468

Johann-S's avatar
Johann-S committed
469
470
      // Adjust body padding
      const actualPadding = document.body.style.paddingRight
471
472
473
474
      const calculatedPadding = window.getComputedStyle(document.body)['padding-right']

      Manipulator.setDataAttribute(document.body, 'padding-right', actualPadding)
      document.body.style.paddingRight = `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`
475
    }
476

477
    document.body.classList.add(ClassName.OPEN)
Johann-S's avatar
Johann-S committed
478
  }
479

Johann-S's avatar
Johann-S committed
480
481
  _resetScrollbar() {
    // Restore fixed content padding
482
    makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
483
      .forEach((element) => {
484
        const padding = Manipulator.getDataAttribute(element, 'padding-right')
485
486
487
488
489
        if (typeof padding !== 'undefined') {
          Manipulator.removeDataAttribute(element, 'padding-right')
          element.style.paddingRight = padding
        }
      })
Johann-S's avatar
Johann-S committed
490

491
    // Restore sticky content and navbar-toggler margin
492
    makeArray(SelectorEngine.find(`${Selector.STICKY_CONTENT}`))
493
      .forEach((element) => {
494
        const margin = Manipulator.getDataAttribute(element, 'margin-right')
495
496
497
498
499
        if (typeof margin !== 'undefined') {
          Manipulator.removeDataAttribute(element, 'margin-right')
          element.style.marginRight = margin
        }
      })
500

Johann-S's avatar
Johann-S committed
501
    // Restore body padding
502
    const padding = Manipulator.getDataAttribute(document.body, 'padding-right')
503
504
505
506
507
508
    if (typeof padding !== 'undefined') {
      Manipulator.removeDataAttribute(document.body, 'padding-right')
      document.body.style.paddingRight = padding
    } else {
      document.body.style.paddingRight = ''
    }
Johann-S's avatar
Johann-S committed
509
  }
510

Johann-S's avatar
Johann-S committed
511
512
513
514
515
516
517
518
  _getScrollbarWidth() { // thx d.walsh
    const scrollDiv = document.createElement('div')
    scrollDiv.className = ClassName.SCROLLBAR_MEASURER
    document.body.appendChild(scrollDiv)
    const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
    document.body.removeChild(scrollDiv)
    return scrollbarWidth
  }
519

Johann-S's avatar
Johann-S committed
520
521
522
523
  // Static

  static _jQueryInterface(config, relatedTarget) {
    return this.each(function () {
524
      let data = Data.getData(this, DATA_KEY)
Johann-S's avatar
Johann-S committed
525
526
      const _config = {
        ...Default,
527
        ...Manipulator.getDataAttributes(this),
Johann-S's avatar
Johann-S committed
528
529
530
531
532
533
        ...typeof config === 'object' && config ? config : {}
      }

      if (!data) {
        data = new Modal(this, _config)
      }
534

Johann-S's avatar
Johann-S committed
535
536
537
      if (typeof config === 'string') {
        if (typeof data[config] === 'undefined') {
          throw new TypeError(`No method named "${config}"`)
538
        }
Johann-S's avatar
Johann-S committed
539
540
541
542
543
        data[config](relatedTarget)
      } else if (_config.show) {
        data.show(relatedTarget)
      }
    })
544
  }
545
546
547
548

  static _getInstance(element) {
    return Data.getData(element, DATA_KEY)
  }
Johann-S's avatar
Johann-S committed
549
550
551
552
553
554
555
}

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

557
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
Johann-S's avatar
Johann-S committed
558
  let target
559
  const selector = getSelectorFromElement(this)
560

Johann-S's avatar
Johann-S committed
561
  if (selector) {
562
    target = SelectorEngine.findOne(selector)
Johann-S's avatar
Johann-S committed
563
  }
564

565
  const config = Data.getData(target, DATA_KEY)
Johann-S's avatar
Johann-S committed
566
    ? 'toggle' : {
567
568
      ...Manipulator.getDataAttributes(target),
      ...Manipulator.getDataAttributes(this)
569
570
    }

Johann-S's avatar
Johann-S committed
571
572
573
  if (this.tagName === 'A' || this.tagName === 'AREA') {
    event.preventDefault()
  }
574

575
576
577
  EventHandler.one(target, Event.SHOW, (showEvent) => {
    if (showEvent.defaultPrevented) {
      // only register focus restorer if modal will actually get shown
Johann-S's avatar
Johann-S committed
578
      return
579
580
    }

581
    EventHandler.one(target, Event.HIDDEN, () => {
582
      if (isVisible(this)) {
Johann-S's avatar
Johann-S committed
583
        this.focus()
584
585
586
587
      }
    })
  })

588
589
590
591
592
593
  let data = Data.getData(target, DATA_KEY)
  if (!data) {
    data = new Modal(target, config)
  }

  data.show(this)
Johann-S's avatar
Johann-S committed
594
595
596
597
598
599
600
})

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

602
603
604
605
606
607
608
609
if (typeof $ !== 'undefined') {
  const JQUERY_NO_CONFLICT = $.fn[NAME]
  $.fn[NAME]               = Modal._jQueryInterface
  $.fn[NAME].Constructor   = Modal
  $.fn[NAME].noConflict    = () => {
    $.fn[NAME] = JQUERY_NO_CONFLICT
    return Modal._jQueryInterface
  }
Johann-S's avatar
Johann-S committed
610
}
611
612

export default Modal