@@ -50,6 +50,7 @@ const DefaultType = {
50
50
html : 'boolean' ,
51
51
selector : '(string|boolean)' ,
52
52
placement : '(string|function)' ,
53
+ offset : '(array|string|function)' ,
53
54
container : '(string|element|boolean)' ,
54
55
fallbackPlacements : 'array' ,
55
56
boundary : '(string|element)' ,
@@ -80,6 +81,7 @@ const Default = {
80
81
html : false ,
81
82
selector : false ,
82
83
placement : 'top' ,
84
+ offset : [ 0 , 0 ] ,
83
85
container : false ,
84
86
fallbackPlacements : [ 'top' , 'right' , 'bottom' , 'left' ] ,
85
87
boundary : 'clippingParents' ,
@@ -191,13 +193,7 @@ class Tooltip extends BaseComponent {
191
193
}
192
194
193
195
if ( event ) {
194
- const dataKey = this . constructor . DATA_KEY
195
- let context = Data . getData ( event . delegateTarget , dataKey )
196
-
197
- if ( ! context ) {
198
- context = new this . constructor ( event . delegateTarget , this . _getDelegateConfig ( ) )
199
- Data . setData ( event . delegateTarget , dataKey , context )
200
- }
196
+ const context = this . _initializeOnDelegatedTarget ( event )
201
197
202
198
context . _activeTrigger . click = ! context . _activeTrigger . click
203
199
@@ -245,83 +241,85 @@ class Tooltip extends BaseComponent {
245
241
throw new Error ( 'Please use show on visible elements' )
246
242
}
247
243
248
- if ( this . isWithContent ( ) && this . _isEnabled ) {
249
- const showEvent = EventHandler . trigger ( this . _element , this . constructor . Event . SHOW )
250
- const shadowRoot = findShadowRoot ( this . _element )
251
- const isInTheDom = shadowRoot === null ?
252
- this . _element . ownerDocument . documentElement . contains ( this . _element ) :
253
- shadowRoot . contains ( this . _element )
244
+ if ( ! ( this . isWithContent ( ) && this . _isEnabled ) ) {
245
+ return
246
+ }
254
247
255
- if ( showEvent . defaultPrevented || ! isInTheDom ) {
256
- return
257
- }
248
+ const showEvent = EventHandler . trigger ( this . _element , this . constructor . Event . SHOW )
249
+ const shadowRoot = findShadowRoot ( this . _element )
250
+ const isInTheDom = shadowRoot === null ?
251
+ this . _element . ownerDocument . documentElement . contains ( this . _element ) :
252
+ shadowRoot . contains ( this . _element )
258
253
259
- const tip = this . getTipElement ( )
260
- const tipId = getUID ( this . constructor . NAME )
254
+ if ( showEvent . defaultPrevented || ! isInTheDom ) {
255
+ return
256
+ }
261
257
262
- tip . setAttribute ( 'id' , tipId )
263
- this . _element . setAttribute ( 'aria-describedby' , tipId )
258
+ const tip = this . getTipElement ( )
259
+ const tipId = getUID ( this . constructor . NAME )
264
260
265
- this . setContent ( )
261
+ tip . setAttribute ( 'id' , tipId )
262
+ this . _element . setAttribute ( 'aria-describedby' , tipId )
266
263
267
- if ( this . config . animation ) {
268
- tip . classList . add ( CLASS_NAME_FADE )
269
- }
264
+ this . setContent ( )
270
265
271
- const placement = typeof this . config . placement === 'function' ?
272
- this . config . placement . call ( this , tip , this . _element ) :
273
- this . config . placement
266
+ if ( this . config . animation ) {
267
+ tip . classList . add ( CLASS_NAME_FADE )
268
+ }
274
269
275
- const attachment = this . _getAttachment ( placement )
276
- this . _addAttachmentClass ( attachment )
270
+ const placement = typeof this . config . placement === 'function' ?
271
+ this . config . placement . call ( this , tip , this . _element ) :
272
+ this . config . placement
277
273
278
- const container = this . _getContainer ( )
279
- Data . setData ( tip , this . constructor . DATA_KEY , this )
274
+ const attachment = this . _getAttachment ( placement )
275
+ this . _addAttachmentClass ( attachment )
280
276
281
- if ( ! this . _element . ownerDocument . documentElement . contains ( this . tip ) ) {
282
- container . appendChild ( tip )
283
- }
277
+ const container = this . _getContainer ( )
278
+ Data . setData ( tip , this . constructor . DATA_KEY , this )
284
279
285
- EventHandler . trigger ( this . _element , this . constructor . Event . INSERTED )
280
+ if ( ! this . _element . ownerDocument . documentElement . contains ( this . tip ) ) {
281
+ container . appendChild ( tip )
282
+ }
286
283
287
- this . _popper = Popper . createPopper ( this . _element , tip , this . _getPopperConfig ( attachment ) )
284
+ EventHandler . trigger ( this . _element , this . constructor . Event . INSERTED )
288
285
289
- tip . classList . add ( CLASS_NAME_SHOW )
286
+ this . _popper = Popper . createPopper ( this . _element , tip , this . _getPopperConfig ( attachment ) )
290
287
291
- const customClass = typeof this . config . customClass === 'function' ? this . config . customClass ( ) : this . config . customClass
292
- if ( customClass ) {
293
- tip . classList . add ( ...customClass . split ( ' ' ) )
294
- }
288
+ tip . classList . add ( CLASS_NAME_SHOW )
295
289
296
- // If this is a touch-enabled device we add extra
297
- // empty mouseover listeners to the body's immediate children;
298
- // only needed because of broken event delegation on iOS
299
- // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
300
- if ( 'ontouchstart' in document . documentElement ) {
301
- [ ] . concat ( ...document . body . children ) . forEach ( element => {
302
- EventHandler . on ( element , 'mouseover' , noop ( ) )
303
- } )
304
- }
290
+ const customClass = typeof this . config . customClass === 'function' ? this . config . customClass ( ) : this . config . customClass
291
+ if ( customClass ) {
292
+ tip . classList . add ( ...customClass . split ( ' ' ) )
293
+ }
305
294
306
- const complete = ( ) => {
307
- const prevHoverState = this . _hoverState
295
+ // If this is a touch-enabled device we add extra
296
+ // empty mouseover listeners to the body's immediate children;
297
+ // only needed because of broken event delegation on iOS
298
+ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
299
+ if ( 'ontouchstart' in document . documentElement ) {
300
+ [ ] . concat ( ...document . body . children ) . forEach ( element => {
301
+ EventHandler . on ( element , 'mouseover' , noop ( ) )
302
+ } )
303
+ }
308
304
309
- this . _hoverState = null
310
- EventHandler . trigger ( this . _element , this . constructor . Event . SHOWN )
305
+ const complete = ( ) => {
306
+ const prevHoverState = this . _hoverState
311
307
312
- if ( prevHoverState === HOVER_STATE_OUT ) {
313
- this . _leave ( null , this )
314
- }
315
- }
308
+ this . _hoverState = null
309
+ EventHandler . trigger ( this . _element , this . constructor . Event . SHOWN )
316
310
317
- if ( this . tip . classList . contains ( CLASS_NAME_FADE ) ) {
318
- const transitionDuration = getTransitionDurationFromElement ( this . tip )
319
- EventHandler . one ( this . tip , 'transitionend' , complete )
320
- emulateTransitionEnd ( this . tip , transitionDuration )
321
- } else {
322
- complete ( )
311
+ if ( prevHoverState === HOVER_STATE_OUT ) {
312
+ this . _leave ( null , this )
323
313
}
324
314
}
315
+
316
+ if ( this . tip . classList . contains ( CLASS_NAME_FADE ) ) {
317
+ const transitionDuration = getTransitionDurationFromElement ( this . tip )
318
+ EventHandler . one ( this . tip , 'transitionend' , complete )
319
+ emulateTransitionEnd ( this . tip , transitionDuration )
320
+ } else {
321
+ complete ( )
322
+ }
325
323
}
326
324
327
325
hide ( ) {
@@ -465,6 +463,32 @@ class Tooltip extends BaseComponent {
465
463
466
464
// Private
467
465
466
+ _initializeOnDelegatedTarget ( event , context ) {
467
+ const dataKey = this . constructor . DATA_KEY
468
+ context = context || Data . getData ( event . delegateTarget , dataKey )
469
+
470
+ if ( ! context ) {
471
+ context = new this . constructor ( event . delegateTarget , this . _getDelegateConfig ( ) )
472
+ Data . setData ( event . delegateTarget , dataKey , context )
473
+ }
474
+
475
+ return context
476
+ }
477
+
478
+ _getOffset ( ) {
479
+ const { offset } = this . config
480
+
481
+ if ( typeof offset === 'string' ) {
482
+ return offset . split ( ',' ) . map ( val => Number . parseInt ( val , 10 ) )
483
+ }
484
+
485
+ if ( typeof offset === 'function' ) {
486
+ return popperData => offset ( popperData , this . _element )
487
+ }
488
+
489
+ return offset
490
+ }
491
+
468
492
_getPopperConfig ( attachment ) {
469
493
const defaultBsConfig = {
470
494
placement : attachment ,
@@ -476,6 +500,12 @@ class Tooltip extends BaseComponent {
476
500
fallbackPlacements : this . config . fallbackPlacements
477
501
}
478
502
} ,
503
+ {
504
+ name : 'offset' ,
505
+ options : {
506
+ offset : this . _getOffset ( )
507
+ }
508
+ } ,
479
509
{
480
510
name : 'preventOverflow' ,
481
511
options : {
@@ -582,16 +612,7 @@ class Tooltip extends BaseComponent {
582
612
}
583
613
584
614
_enter ( event , context ) {
585
- const dataKey = this . constructor . DATA_KEY
586
- context = context || Data . getData ( event . delegateTarget , dataKey )
587
-
588
- if ( ! context ) {
589
- context = new this . constructor (
590
- event . delegateTarget ,
591
- this . _getDelegateConfig ( )
592
- )
593
- Data . setData ( event . delegateTarget , dataKey , context )
594
- }
615
+ context = this . _initializeOnDelegatedTarget ( event , context )
595
616
596
617
if ( event ) {
597
618
context . _activeTrigger [
@@ -621,16 +642,7 @@ class Tooltip extends BaseComponent {
621
642
}
622
643
623
644
_leave ( event , context ) {
624
- const dataKey = this . constructor . DATA_KEY
625
- context = context || Data . getData ( event . delegateTarget , dataKey )
626
-
627
- if ( ! context ) {
628
- context = new this . constructor (
629
- event . delegateTarget ,
630
- this . _getDelegateConfig ( )
631
- )
632
- Data . setData ( event . delegateTarget , dataKey , context )
633
- }
645
+ context = this . _initializeOnDelegatedTarget ( event , context )
634
646
635
647
if ( event ) {
636
648
context . _activeTrigger [
0 commit comments