@rhavenside/baseline 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +472 -0
  2. package/dist/base-line.css +5 -0
  3. package/dist/base-line.css.map +1 -0
  4. package/dist/fonts/GoogleSansCode-Bold.ttf +0 -0
  5. package/dist/fonts/GoogleSansCode-BoldItalic.ttf +0 -0
  6. package/dist/fonts/GoogleSansCode-ExtraBold.ttf +0 -0
  7. package/dist/fonts/GoogleSansCode-ExtraBoldItalic.ttf +0 -0
  8. package/dist/fonts/GoogleSansCode-Italic-VariableFont_wght.ttf +0 -0
  9. package/dist/fonts/GoogleSansCode-Italic.ttf +0 -0
  10. package/dist/fonts/GoogleSansCode-Light.ttf +0 -0
  11. package/dist/fonts/GoogleSansCode-LightItalic.ttf +0 -0
  12. package/dist/fonts/GoogleSansCode-Medium.ttf +0 -0
  13. package/dist/fonts/GoogleSansCode-MediumItalic.ttf +0 -0
  14. package/dist/fonts/GoogleSansCode-Regular.ttf +0 -0
  15. package/dist/fonts/GoogleSansCode-SemiBold.ttf +0 -0
  16. package/dist/fonts/GoogleSansCode-SemiBoldItalic.ttf +0 -0
  17. package/dist/fonts/GoogleSansCode-VariableFont_wght.ttf +0 -0
  18. package/dist/fonts/RobotoCondensed-Black.ttf +0 -0
  19. package/dist/fonts/RobotoCondensed-BlackItalic.ttf +0 -0
  20. package/dist/fonts/RobotoCondensed-Bold.ttf +0 -0
  21. package/dist/fonts/RobotoCondensed-BoldItalic.ttf +0 -0
  22. package/dist/fonts/RobotoCondensed-ExtraBold.ttf +0 -0
  23. package/dist/fonts/RobotoCondensed-ExtraBoldItalic.ttf +0 -0
  24. package/dist/fonts/RobotoCondensed-ExtraLight.ttf +0 -0
  25. package/dist/fonts/RobotoCondensed-ExtraLightItalic.ttf +0 -0
  26. package/dist/fonts/RobotoCondensed-Italic-VariableFont_wght.ttf +0 -0
  27. package/dist/fonts/RobotoCondensed-Italic.ttf +0 -0
  28. package/dist/fonts/RobotoCondensed-Light.ttf +0 -0
  29. package/dist/fonts/RobotoCondensed-LightItalic.ttf +0 -0
  30. package/dist/fonts/RobotoCondensed-Medium.ttf +0 -0
  31. package/dist/fonts/RobotoCondensed-MediumItalic.ttf +0 -0
  32. package/dist/fonts/RobotoCondensed-Regular.ttf +0 -0
  33. package/dist/fonts/RobotoCondensed-SemiBold.ttf +0 -0
  34. package/dist/fonts/RobotoCondensed-SemiBoldItalic.ttf +0 -0
  35. package/dist/fonts/RobotoCondensed-Thin.ttf +0 -0
  36. package/dist/fonts/RobotoCondensed-ThinItalic.ttf +0 -0
  37. package/dist/fonts/RobotoCondensed-VariableFont_wght.ttf +0 -0
  38. package/dist/fonts/ZalandoSansExpanded-Black.ttf +0 -0
  39. package/dist/fonts/ZalandoSansExpanded-BlackItalic.ttf +0 -0
  40. package/dist/fonts/ZalandoSansExpanded-Bold.ttf +0 -0
  41. package/dist/fonts/ZalandoSansExpanded-BoldItalic.ttf +0 -0
  42. package/dist/fonts/ZalandoSansExpanded-ExtraBold.ttf +0 -0
  43. package/dist/fonts/ZalandoSansExpanded-ExtraBoldItalic.ttf +0 -0
  44. package/dist/fonts/ZalandoSansExpanded-ExtraLight.ttf +0 -0
  45. package/dist/fonts/ZalandoSansExpanded-ExtraLightItalic.ttf +0 -0
  46. package/dist/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf +0 -0
  47. package/dist/fonts/ZalandoSansExpanded-Italic.ttf +0 -0
  48. package/dist/fonts/ZalandoSansExpanded-Light.ttf +0 -0
  49. package/dist/fonts/ZalandoSansExpanded-LightItalic.ttf +0 -0
  50. package/dist/fonts/ZalandoSansExpanded-Medium.ttf +0 -0
  51. package/dist/fonts/ZalandoSansExpanded-MediumItalic.ttf +0 -0
  52. package/dist/fonts/ZalandoSansExpanded-Regular.ttf +0 -0
  53. package/dist/fonts/ZalandoSansExpanded-SemiBold.ttf +0 -0
  54. package/dist/fonts/ZalandoSansExpanded-SemiBoldItalic.ttf +0 -0
  55. package/dist/fonts/ZalandoSansExpanded-VariableFont_wght.ttf +0 -0
  56. package/dist/fonts/baseline-icons.woff +0 -0
  57. package/dist/fonts/baseline-icons.woff2 +0 -0
  58. package/dist/js/accordion.js +103 -0
  59. package/dist/js/alert.js +91 -0
  60. package/dist/js/base.js +146 -0
  61. package/dist/js/button.js +80 -0
  62. package/dist/js/carousel.js +427 -0
  63. package/dist/js/collapse.js +233 -0
  64. package/dist/js/color-modes.js +70 -0
  65. package/dist/js/component.js +114 -0
  66. package/dist/js/dropdown.js +348 -0
  67. package/dist/js/index.js +108 -0
  68. package/dist/js/modal.js +440 -0
  69. package/dist/js/offcanvas.js +356 -0
  70. package/dist/js/popover.js +241 -0
  71. package/dist/js/swipe.js +143 -0
  72. package/dist/js/tab.js +285 -0
  73. package/dist/js/toast.js +228 -0
  74. package/dist/js/tooltip.js +716 -0
  75. package/dist/js/util/backdrop.js +133 -0
  76. package/dist/js/util/component-functions.js +111 -0
  77. package/dist/js/util/focustrap.js +101 -0
  78. package/dist/js/util/scrollbar.js +111 -0
  79. package/dist/js/util.js +564 -0
  80. package/package.json +47 -0
@@ -0,0 +1,716 @@
1
+ /**
2
+ * Base-Line Tooltip Component
3
+ * Baseline 2.0 compatible tooltip with Base-Line naming
4
+ * Based on Baseline-main architecture
5
+ */
6
+
7
+ import * as Popper from '@popperjs/core'
8
+ import { BaseComponent } from './base.js'
9
+ import { EventHandler } from './util.js'
10
+ import { getElement, isRTL, defineJQueryPlugin } from './util.js'
11
+
12
+ const NAME = 'tooltip'
13
+ const DATA_KEY = `c.${NAME}`
14
+ const EVENT_KEY = `.${DATA_KEY}`
15
+
16
+ const CLASS_NAME_FADE = 'c-fade'
17
+ const CLASS_NAME_SHOW = 'is-show'
18
+
19
+ const SELECTOR_TOOLTIP_INNER = '.c-tooltip-inner'
20
+ const SELECTOR_MODAL = '.c-modal'
21
+
22
+ const EVENT_MODAL_HIDE = 'hide.c.modal'
23
+
24
+ const TRIGGER_HOVER = 'hover'
25
+ const TRIGGER_FOCUS = 'focus'
26
+ const TRIGGER_CLICK = 'click'
27
+ const TRIGGER_MANUAL = 'manual'
28
+
29
+ const EVENT_HIDE = 'hide'
30
+ const EVENT_HIDDEN = 'hidden'
31
+ const EVENT_SHOW = 'show'
32
+ const EVENT_SHOWN = 'shown'
33
+ const EVENT_INSERTED = 'inserted'
34
+ const EVENT_CLICK = 'click'
35
+ const EVENT_FOCUSIN = 'focusin'
36
+ const EVENT_FOCUSOUT = 'focusout'
37
+ const EVENT_MOUSEENTER = 'mouseenter'
38
+ const EVENT_MOUSELEAVE = 'mouseleave'
39
+
40
+ const AttachmentMap = {
41
+ AUTO: 'auto',
42
+ TOP: 'top',
43
+ RIGHT: isRTL() ? 'left' : 'right',
44
+ BOTTOM: 'bottom',
45
+ LEFT: isRTL() ? 'right' : 'left'
46
+ }
47
+
48
+ const Default = {
49
+ allowList: {},
50
+ animation: true,
51
+ boundary: 'clippingParents',
52
+ container: false,
53
+ customClass: '',
54
+ delay: 0,
55
+ fallbackPlacements: ['top', 'right', 'bottom', 'left'],
56
+ html: false,
57
+ offset: [0, 6],
58
+ placement: 'top',
59
+ popperConfig: null,
60
+ sanitize: true,
61
+ sanitizeFn: null,
62
+ selector: false,
63
+ template: '<div class="c-tooltip" role="tooltip">' +
64
+ '<div class="c-tooltip-arrow"></div>' +
65
+ '<div class="c-tooltip-inner"></div>' +
66
+ '</div>',
67
+ title: '',
68
+ trigger: 'hover focus'
69
+ }
70
+
71
+ const DefaultType = {
72
+ allowList: 'object',
73
+ animation: 'boolean',
74
+ boundary: '(string|element)',
75
+ container: '(string|element|boolean)',
76
+ customClass: '(string|function)',
77
+ delay: '(number|object)',
78
+ fallbackPlacements: 'array',
79
+ html: 'boolean',
80
+ offset: '(array|string|function)',
81
+ placement: '(string|function)',
82
+ popperConfig: '(null|object|function)',
83
+ sanitize: 'boolean',
84
+ sanitizeFn: '(null|function)',
85
+ selector: '(string|boolean)',
86
+ template: 'string',
87
+ title: '(string|element|function)',
88
+ trigger: 'string'
89
+ }
90
+
91
+ /**
92
+ * Helper to create event name with prefix
93
+ */
94
+ function eventName(name) {
95
+ return `${name}${EVENT_KEY}`
96
+ }
97
+
98
+ /**
99
+ * Helper to execute function or return value
100
+ */
101
+ function execute(fn, args = []) {
102
+ if (typeof fn === 'function') {
103
+ return fn(...args)
104
+ }
105
+ return fn
106
+ }
107
+
108
+ /**
109
+ * Generate unique ID
110
+ */
111
+ let uidCounter = 0
112
+ function getUID(prefix = '') {
113
+ uidCounter++
114
+ return `${prefix}${uidCounter}`
115
+ }
116
+
117
+ /**
118
+ * Find shadow root
119
+ */
120
+ function findShadowRoot(element) {
121
+ if (!document.documentElement.attachShadow) {
122
+ return null
123
+ }
124
+
125
+ if (typeof element.getRootNode === 'function') {
126
+ const root = element.getRootNode()
127
+ return root instanceof ShadowRoot ? root : null
128
+ }
129
+
130
+ if (element instanceof ShadowRoot) {
131
+ return element
132
+ }
133
+
134
+ if (!element.parentNode) {
135
+ return null
136
+ }
137
+
138
+ return findShadowRoot(element.parentNode)
139
+ }
140
+
141
+ /**
142
+ * Class definition
143
+ */
144
+
145
+ class Tooltip extends BaseComponent {
146
+ constructor(element, config) {
147
+ if (typeof Popper === 'undefined') {
148
+ throw new TypeError('Base-Line\'s tooltips require Popper (https://popper.js.org/docs/v2/)')
149
+ }
150
+
151
+ super(element, config)
152
+
153
+ // Private
154
+ this._isEnabled = true
155
+ this._timeout = 0
156
+ this._isHovered = null
157
+ this._activeTrigger = {}
158
+ this._popper = null
159
+ this._newContent = null
160
+
161
+ // Protected
162
+ this.tip = null
163
+
164
+ this._setListeners()
165
+
166
+ if (!this._config.selector) {
167
+ this._fixTitle()
168
+ }
169
+ }
170
+
171
+ // Getters
172
+ static get Default() {
173
+ return Default
174
+ }
175
+
176
+ static get DefaultType() {
177
+ return DefaultType
178
+ }
179
+
180
+ static get NAME() {
181
+ return NAME
182
+ }
183
+
184
+ static eventName(name) {
185
+ return eventName(name)
186
+ }
187
+
188
+ // Public
189
+ enable() {
190
+ this._isEnabled = true
191
+ }
192
+
193
+ disable() {
194
+ this._isEnabled = false
195
+ }
196
+
197
+ toggleEnabled() {
198
+ this._isEnabled = !this._isEnabled
199
+ }
200
+
201
+ toggle() {
202
+ if (!this._isEnabled) {
203
+ return
204
+ }
205
+
206
+ if (this._isShown()) {
207
+ this._leave()
208
+ return
209
+ }
210
+
211
+ this._enter()
212
+ }
213
+
214
+ dispose() {
215
+ clearTimeout(this._timeout)
216
+
217
+ const modal = this._element?.closest(SELECTOR_MODAL)
218
+ if (modal) {
219
+ EventHandler.off(modal, EVENT_MODAL_HIDE, this._hideModalHandler)
220
+ }
221
+
222
+ if (this._element?.getAttribute('data-c-original-title')) {
223
+ this._element.setAttribute('title', this._element.getAttribute('data-c-original-title'))
224
+ }
225
+
226
+ this._disposePopper()
227
+ super.dispose()
228
+ }
229
+
230
+ show() {
231
+ if (this._element.style.display === 'none') {
232
+ throw new Error('Please use show on visible elements')
233
+ }
234
+
235
+ if (!(this._isWithContent() && this._isEnabled)) {
236
+ return
237
+ }
238
+
239
+ const showEvent = EventHandler.trigger(this._element, eventName(EVENT_SHOW))
240
+ const shadowRoot = findShadowRoot(this._element)
241
+ const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)
242
+
243
+ if (showEvent.defaultPrevented || !isInTheDom) {
244
+ return
245
+ }
246
+
247
+ // TODO: v6 remove this or make it optional
248
+ this._disposePopper()
249
+
250
+ const tip = this._getTipElement()
251
+
252
+ this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
253
+
254
+ const { container } = this._config
255
+
256
+ if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
257
+ container.append(tip)
258
+ EventHandler.trigger(this._element, eventName(EVENT_INSERTED))
259
+ }
260
+
261
+ this._popper = this._createPopper(tip)
262
+
263
+ tip.classList.add(CLASS_NAME_SHOW)
264
+
265
+ // If this is a touch-enabled device we add extra
266
+ // empty mouseover listeners to the body's immediate children;
267
+ // only needed because of broken event delegation on iOS
268
+ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
269
+ if ('ontouchstart' in document.documentElement) {
270
+ for (const element of [].concat(...document.body.children)) {
271
+ EventHandler.on(element, 'mouseover', () => {})
272
+ }
273
+ }
274
+
275
+ const complete = () => {
276
+ EventHandler.trigger(this._element, eventName(EVENT_SHOWN))
277
+
278
+ if (this._isHovered === false) {
279
+ this._leave()
280
+ }
281
+
282
+ this._isHovered = false
283
+ }
284
+
285
+ this._queueCallback(complete, this.tip, this._isAnimated())
286
+ }
287
+
288
+ hide() {
289
+ if (!this._isShown()) {
290
+ return
291
+ }
292
+
293
+ const hideEvent = EventHandler.trigger(this._element, eventName(EVENT_HIDE))
294
+ if (hideEvent.defaultPrevented) {
295
+ return
296
+ }
297
+
298
+ const tip = this._getTipElement()
299
+ tip.classList.remove(CLASS_NAME_SHOW)
300
+
301
+ // If this is a touch-enabled device we remove the extra
302
+ // empty mouseover listeners we added for iOS support
303
+ if ('ontouchstart' in document.documentElement) {
304
+ for (const element of [].concat(...document.body.children)) {
305
+ EventHandler.off(element, 'mouseover', () => {})
306
+ }
307
+ }
308
+
309
+ this._activeTrigger[TRIGGER_CLICK] = false
310
+ this._activeTrigger[TRIGGER_FOCUS] = false
311
+ this._activeTrigger[TRIGGER_HOVER] = false
312
+ this._isHovered = null // it is a trick to support manual triggering
313
+
314
+ const complete = () => {
315
+ if (this._isWithActiveTrigger()) {
316
+ return
317
+ }
318
+
319
+ if (!this._isHovered) {
320
+ this._disposePopper()
321
+ }
322
+
323
+ this._element.removeAttribute('aria-describedby')
324
+ EventHandler.trigger(this._element, eventName(EVENT_HIDDEN))
325
+ }
326
+
327
+ this._queueCallback(complete, this.tip, this._isAnimated())
328
+ }
329
+
330
+ update() {
331
+ if (this._popper) {
332
+ this._popper.update()
333
+ }
334
+ }
335
+
336
+ // Protected
337
+ _isWithContent() {
338
+ return Boolean(this._getTitle())
339
+ }
340
+
341
+ _getTipElement() {
342
+ if (!this.tip) {
343
+ this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())
344
+ }
345
+
346
+ return this.tip
347
+ }
348
+
349
+ _createTipElement(content) {
350
+ const template = document.createElement('div')
351
+ template.innerHTML = this._config.template.trim()
352
+ const tip = template.firstChild
353
+
354
+ tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
355
+ // TODO: v6 the following can be achieved with CSS only
356
+ tip.classList.add(`c-${this.constructor.NAME}-auto`)
357
+
358
+ const tipId = getUID(this.constructor.NAME).toString()
359
+
360
+ tip.setAttribute('id', tipId)
361
+
362
+ if (this._isAnimated()) {
363
+ tip.classList.add(CLASS_NAME_FADE)
364
+ }
365
+
366
+ // Set content
367
+ if (content) {
368
+ const inner = tip.querySelector(SELECTOR_TOOLTIP_INNER)
369
+ if (inner) {
370
+ if (this._config.html) {
371
+ inner.innerHTML = content[SELECTOR_TOOLTIP_INNER] || content
372
+ } else {
373
+ inner.textContent = content[SELECTOR_TOOLTIP_INNER] || content
374
+ }
375
+ }
376
+ }
377
+
378
+ return tip
379
+ }
380
+
381
+ setContent(content) {
382
+ this._newContent = content
383
+ if (this._isShown()) {
384
+ this._disposePopper()
385
+ this.show()
386
+ }
387
+ }
388
+
389
+ _getContentForTemplate() {
390
+ return {
391
+ [SELECTOR_TOOLTIP_INNER]: this._getTitle()
392
+ }
393
+ }
394
+
395
+ _getTitle() {
396
+ return execute(this._config.title) || this._element.getAttribute('data-c-original-title')
397
+ }
398
+
399
+ // Private
400
+ _initializeOnDelegatedTarget(event) {
401
+ // In base-line, delegated events use handler.call(target, e), so 'this' is the matched element
402
+ // event.delegateTarget might not be set, so we use 'this' as fallback
403
+ // When called from a delegated handler, 'this' refers to the matched element
404
+ const target = event.delegateTarget || (typeof this === 'object' && this !== null && this.nodeType ? this : null)
405
+ if (!target) {
406
+ return this
407
+ }
408
+ return this.constructor.getOrCreateInstance(target, this._getDelegateConfig())
409
+ }
410
+
411
+ _isAnimated() {
412
+ return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))
413
+ }
414
+
415
+ _isShown() {
416
+ return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)
417
+ }
418
+
419
+ _createPopper(tip) {
420
+ const placement = execute(this._config.placement, [this, tip, this._element])
421
+ const attachment = AttachmentMap[placement.toUpperCase()] || placement
422
+ return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
423
+ }
424
+
425
+ _getOffset() {
426
+ const { offset } = this._config
427
+
428
+ if (typeof offset === 'string') {
429
+ return offset.split(',').map(value => Number.parseInt(value, 10))
430
+ }
431
+
432
+ if (typeof offset === 'function') {
433
+ return popperData => offset(popperData, this._element)
434
+ }
435
+
436
+ return offset
437
+ }
438
+
439
+ _resolvePossibleFunction(arg) {
440
+ return execute(arg, [this._element, this._element])
441
+ }
442
+
443
+ _getPopperConfig(attachment) {
444
+ const defaultBsPopperConfig = {
445
+ placement: attachment,
446
+ modifiers: [
447
+ {
448
+ name: 'flip',
449
+ options: {
450
+ fallbackPlacements: this._config.fallbackPlacements
451
+ }
452
+ },
453
+ {
454
+ name: 'offset',
455
+ options: {
456
+ offset: this._getOffset()
457
+ }
458
+ },
459
+ {
460
+ name: 'preventOverflow',
461
+ options: {
462
+ boundary: this._config.boundary
463
+ }
464
+ },
465
+ {
466
+ name: 'arrow',
467
+ options: {
468
+ element: `.c-tooltip-arrow`
469
+ }
470
+ },
471
+ {
472
+ name: 'preSetPlacement',
473
+ enabled: true,
474
+ phase: 'beforeMain',
475
+ fn: data => {
476
+ // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
477
+ // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
478
+ this._getTipElement().setAttribute('data-popper-placement', data.state.placement)
479
+ }
480
+ }
481
+ ]
482
+ }
483
+
484
+ return {
485
+ ...defaultBsPopperConfig,
486
+ ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
487
+ }
488
+ }
489
+
490
+ _setListeners() {
491
+ const triggers = this._config.trigger.split(' ')
492
+
493
+ for (const trigger of triggers) {
494
+ if (trigger === TRIGGER_CLICK) {
495
+ if (this._config.selector) {
496
+ EventHandler.on(this._element, eventName(EVENT_CLICK), this._config.selector, event => {
497
+ const context = this._initializeOnDelegatedTarget(event)
498
+ context._activeTrigger[TRIGGER_CLICK] = !(context._isShown() && context._activeTrigger[TRIGGER_CLICK])
499
+ context.toggle()
500
+ })
501
+ } else {
502
+ EventHandler.on(this._element, eventName(EVENT_CLICK), event => {
503
+ this._activeTrigger[TRIGGER_CLICK] = !(this._isShown() && this._activeTrigger[TRIGGER_CLICK])
504
+ this.toggle()
505
+ })
506
+ }
507
+ } else if (trigger !== TRIGGER_MANUAL) {
508
+ const eventIn = trigger === TRIGGER_HOVER ?
509
+ eventName(EVENT_MOUSEENTER) :
510
+ eventName(EVENT_FOCUSIN)
511
+ const eventOut = trigger === TRIGGER_HOVER ?
512
+ eventName(EVENT_MOUSELEAVE) :
513
+ eventName(EVENT_FOCUSOUT)
514
+
515
+ if (this._config.selector) {
516
+ EventHandler.on(this._element, eventIn, this._config.selector, event => {
517
+ const context = this._initializeOnDelegatedTarget(event)
518
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true
519
+ context._enter()
520
+ })
521
+ EventHandler.on(this._element, eventOut, this._config.selector, event => {
522
+ const context = this._initializeOnDelegatedTarget(event)
523
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =
524
+ context._element.contains(event.relatedTarget)
525
+
526
+ context._leave()
527
+ })
528
+ } else {
529
+ EventHandler.on(this._element, eventIn, event => {
530
+ this._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true
531
+ this._enter()
532
+ })
533
+ EventHandler.on(this._element, eventOut, event => {
534
+ this._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =
535
+ this._element.contains(event.relatedTarget)
536
+
537
+ this._leave()
538
+ })
539
+ }
540
+ }
541
+ }
542
+
543
+ this._hideModalHandler = () => {
544
+ if (this._element) {
545
+ this.hide()
546
+ }
547
+ }
548
+
549
+ const modal = this._element.closest(SELECTOR_MODAL)
550
+ if (modal) {
551
+ EventHandler.on(modal, EVENT_MODAL_HIDE, this._hideModalHandler)
552
+ }
553
+ }
554
+
555
+ _fixTitle() {
556
+ const title = this._element.getAttribute('title')
557
+
558
+ if (!title) {
559
+ return
560
+ }
561
+
562
+ if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {
563
+ this._element.setAttribute('aria-label', title)
564
+ }
565
+
566
+ this._element.setAttribute('data-c-original-title', title) // DO NOT USE IT. Is only for backwards compatibility
567
+ this._element.removeAttribute('title')
568
+ }
569
+
570
+ _enter() {
571
+ if (this._isShown() || this._isHovered) {
572
+ this._isHovered = true
573
+ return
574
+ }
575
+
576
+ this._isHovered = true
577
+
578
+ this._setTimeout(() => {
579
+ if (this._isHovered) {
580
+ this.show()
581
+ }
582
+ }, this._config.delay.show)
583
+ }
584
+
585
+ _leave() {
586
+ if (this._isWithActiveTrigger()) {
587
+ return
588
+ }
589
+
590
+ this._isHovered = false
591
+
592
+ this._setTimeout(() => {
593
+ if (!this._isHovered) {
594
+ this.hide()
595
+ }
596
+ }, this._config.delay.hide)
597
+ }
598
+
599
+ _setTimeout(handler, timeout) {
600
+ clearTimeout(this._timeout)
601
+ this._timeout = setTimeout(handler, timeout)
602
+ }
603
+
604
+ _isWithActiveTrigger() {
605
+ return Object.values(this._activeTrigger).includes(true)
606
+ }
607
+
608
+ _configAfterMerge(config) {
609
+ config.container = config.container === false ? document.body : getElement(config.container)
610
+
611
+ if (typeof config.delay === 'number') {
612
+ config.delay = {
613
+ show: config.delay,
614
+ hide: config.delay
615
+ }
616
+ }
617
+
618
+ if (typeof config.title === 'number') {
619
+ config.title = config.title.toString()
620
+ }
621
+
622
+ return config
623
+ }
624
+
625
+ _getDelegateConfig() {
626
+ const config = {}
627
+
628
+ for (const [key, value] of Object.entries(this._config)) {
629
+ if (this.constructor.Default[key] !== value) {
630
+ config[key] = value
631
+ }
632
+ }
633
+
634
+ config.selector = false
635
+ config.trigger = 'manual'
636
+
637
+ return config
638
+ }
639
+
640
+ _disposePopper() {
641
+ if (this._popper) {
642
+ this._popper.destroy()
643
+ this._popper = null
644
+ }
645
+
646
+ if (this.tip) {
647
+ this.tip.remove()
648
+ this.tip = null
649
+ }
650
+ }
651
+
652
+ // Static
653
+ static getOrCreateInstance(element, config = {}) {
654
+ return element._baseLineComponent || new Tooltip(element, config)
655
+ }
656
+
657
+ static getInstance(element) {
658
+ return element._baseLineComponent || null
659
+ }
660
+
661
+ static jQueryInterface(config) {
662
+ return this.each(function () {
663
+ const data = Tooltip.getOrCreateInstance(this, config)
664
+
665
+ if (typeof config !== 'string') {
666
+ return
667
+ }
668
+
669
+ if (typeof data[config] === 'undefined') {
670
+ throw new TypeError(`No method named "${config}"`)
671
+ }
672
+
673
+ data[config]()
674
+ })
675
+ }
676
+ }
677
+
678
+ /**
679
+ * Data API implementation
680
+ */
681
+
682
+ // Support both data-c-toggle="tooltip" and data-bl-toggle="tooltip"
683
+ const SELECTOR_DATA_TOGGLE = '[data-c-toggle="tooltip"]'
684
+ const SELECTOR_DATA_TOGGLE_BS = '[data-bl-toggle="tooltip"]'
685
+
686
+ // Initialize tooltips on DOM ready
687
+ import { onDOMContentLoaded } from './util.js'
688
+
689
+ onDOMContentLoaded(() => {
690
+ const tooltipElements = document.querySelectorAll(`${SELECTOR_DATA_TOGGLE}, ${SELECTOR_DATA_TOGGLE_BS}`)
691
+ tooltipElements.forEach(element => {
692
+ if (!element._baseLineComponent) {
693
+ const title = element.getAttribute('title') ||
694
+ element.getAttribute('data-c-title') ||
695
+ element.getAttribute('data-bl-title')
696
+ const placement = element.getAttribute('data-c-placement') ||
697
+ element.getAttribute('data-bl-placement') ||
698
+ 'top'
699
+
700
+ if (title) {
701
+ Tooltip.getOrCreateInstance(element, {
702
+ title: title,
703
+ placement: placement
704
+ })
705
+ }
706
+ }
707
+ })
708
+ })
709
+
710
+ /**
711
+ * jQuery
712
+ */
713
+
714
+ defineJQueryPlugin(Tooltip)
715
+
716
+ export default Tooltip