@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.
- package/README.md +472 -0
- package/dist/base-line.css +5 -0
- package/dist/base-line.css.map +1 -0
- package/dist/fonts/GoogleSansCode-Bold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-BoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-ExtraBold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Italic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Light.ttf +0 -0
- package/dist/fonts/GoogleSansCode-LightItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/dist/fonts/GoogleSansCode-MediumItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/dist/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-VariableFont_wght.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Black.ttf +0 -0
- package/dist/fonts/RobotoCondensed-BlackItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Bold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-BoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraBold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraLight.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraLightItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Italic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Light.ttf +0 -0
- package/dist/fonts/RobotoCondensed-LightItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Medium.ttf +0 -0
- package/dist/fonts/RobotoCondensed-MediumItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Regular.ttf +0 -0
- package/dist/fonts/RobotoCondensed-SemiBold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Thin.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ThinItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-VariableFont_wght.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Black.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-BlackItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Bold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-BoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraBold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraLight.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraLightItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Italic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Light.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-LightItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Medium.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-MediumItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Regular.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-SemiBold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-VariableFont_wght.ttf +0 -0
- package/dist/fonts/baseline-icons.woff +0 -0
- package/dist/fonts/baseline-icons.woff2 +0 -0
- package/dist/js/accordion.js +103 -0
- package/dist/js/alert.js +91 -0
- package/dist/js/base.js +146 -0
- package/dist/js/button.js +80 -0
- package/dist/js/carousel.js +427 -0
- package/dist/js/collapse.js +233 -0
- package/dist/js/color-modes.js +70 -0
- package/dist/js/component.js +114 -0
- package/dist/js/dropdown.js +348 -0
- package/dist/js/index.js +108 -0
- package/dist/js/modal.js +440 -0
- package/dist/js/offcanvas.js +356 -0
- package/dist/js/popover.js +241 -0
- package/dist/js/swipe.js +143 -0
- package/dist/js/tab.js +285 -0
- package/dist/js/toast.js +228 -0
- package/dist/js/tooltip.js +716 -0
- package/dist/js/util/backdrop.js +133 -0
- package/dist/js/util/component-functions.js +111 -0
- package/dist/js/util/focustrap.js +101 -0
- package/dist/js/util/scrollbar.js +111 -0
- package/dist/js/util.js +564 -0
- 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
|