]>
git.ipfire.org Git - ipfire.org.git/blob - src/scss/bootstrap-4.0.0-alpha.6/js/src/tooltip.js
3 import Util
from './util'
7 * --------------------------------------------------------------------------
8 * Bootstrap (v4.0.0-alpha.6): tooltip.js
9 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
10 * --------------------------------------------------------------------------
13 const Tooltip
= (($) => {
16 * Check for Tether dependency
17 * Tether - http://tether.io/
19 if (typeof Tether
=== 'undefined') {
20 throw new Error('Bootstrap tooltips require Tether (http://tether.io/)')
25 * ------------------------------------------------------------------------
27 * ------------------------------------------------------------------------
30 const NAME
= 'tooltip'
31 const VERSION
= '4.0.0-alpha.6'
32 const DATA_KEY
= 'bs.tooltip'
33 const EVENT_KEY
= `.${DATA_KEY}`
34 const JQUERY_NO_CONFLICT
= $.fn
[NAME
]
35 const TRANSITION_DURATION
= 150
36 const CLASS_PREFIX
= 'bs-tether'
40 template
: '<div class="tooltip" role="tooltip">'
41 + '<div class="tooltip-inner"></div></div>',
42 trigger
: 'hover focus',
54 animation
: 'boolean',
56 title
: '(string|element|function)',
58 delay
: '(number|object)',
60 selector
: '(string|boolean)',
61 placement
: '(string|function)',
63 constraints
: 'array',
64 container
: '(string|element|boolean)'
67 const AttachmentMap
= {
68 TOP
: 'bottom center',
69 RIGHT
: 'middle left',
70 BOTTOM
: 'top center',
80 HIDE
: `hide${EVENT_KEY}`,
81 HIDDEN
: `hidden${EVENT_KEY}`,
82 SHOW
: `show${EVENT_KEY}`,
83 SHOWN
: `shown${EVENT_KEY}`,
84 INSERTED
: `inserted${EVENT_KEY}`,
85 CLICK
: `click${EVENT_KEY}`,
86 FOCUSIN
: `focusin${EVENT_KEY}`,
87 FOCUSOUT
: `focusout${EVENT_KEY}`,
88 MOUSEENTER
: `mouseenter${EVENT_KEY}`,
89 MOUSELEAVE
: `mouseleave${EVENT_KEY}`
99 TOOLTIP_INNER
: '.tooltip-inner'
102 const TetherClass
= {
116 * ------------------------------------------------------------------------
118 * ------------------------------------------------------------------------
123 constructor(element
, config
) {
126 this._isEnabled
= true
128 this._hoverState
= ''
129 this._activeTrigger
= {}
130 this._isTransitioning
= false
134 this.element
= element
135 this.config
= this._getConfig(config
)
145 static get VERSION() {
149 static get Default() {
157 static get DATA_KEY() {
165 static get EVENT_KEY() {
169 static get DefaultType() {
177 this._isEnabled
= true
181 this._isEnabled
= false
185 this._isEnabled
= !this._isEnabled
190 const dataKey
= this.constructor.DATA_KEY
191 let context
= $(event
.currentTarget
).data(dataKey
)
194 context
= new this.constructor(
196 this._getDelegateConfig()
198 $(event
.currentTarget
).data(dataKey
, context
)
201 context
._activeTrigger
.click
= !context
._activeTrigger
.click
203 if (context
._isWithActiveTrigger()) {
204 context
._enter(null, context
)
206 context
._leave(null, context
)
211 if ($(this.getTipElement()).hasClass(ClassName
.SHOW
)) {
212 this._leave(null, this)
216 this._enter(null, this)
221 clearTimeout(this._timeout
)
225 $.removeData(this.element
, this.constructor.DATA_KEY
)
227 $(this.element
).off(this.constructor.EVENT_KEY
)
228 $(this.element
).closest('.modal').off('hide.bs.modal')
234 this._isEnabled
= null
236 this._hoverState
= null
237 this._activeTrigger
= null
246 if ($(this.element
).css('display') === 'none') {
247 throw new Error('Please use show on visible elements')
250 const showEvent
= $.Event(this.constructor.Event
.SHOW
)
251 if (this.isWithContent() && this._isEnabled
) {
252 if (this._isTransitioning
) {
253 throw new Error('Tooltip is transitioning')
255 $(this.element
).trigger(showEvent
)
257 const isInTheDom
= $.contains(
258 this.element
.ownerDocument
.documentElement
,
262 if (showEvent
.isDefaultPrevented() || !isInTheDom
) {
266 const tip
= this.getTipElement()
267 const tipId
= Util
.getUID(this.constructor.NAME
)
269 tip
.setAttribute('id', tipId
)
270 this.element
.setAttribute('aria-describedby', tipId
)
274 if (this.config
.animation
) {
275 $(tip
).addClass(ClassName
.FADE
)
278 const placement
= typeof this.config
.placement
=== 'function' ?
279 this.config
.placement
.call(this, tip
, this.element
) :
280 this.config
.placement
282 const attachment
= this._getAttachment(placement
)
284 const container
= this.config
.container
=== false ? document
.body
: $(this.config
.container
)
287 .data(this.constructor.DATA_KEY
, this)
290 $(this.element
).trigger(this.constructor.Event
.INSERTED
)
292 this._tether
= new Tether({
295 target
: this.element
,
296 classes
: TetherClass
,
297 classPrefix
: CLASS_PREFIX
,
298 offset
: this.config
.offset
,
299 constraints
: this.config
.constraints
,
300 addTargetClasses
: false
304 this._tether
.position()
306 $(tip
).addClass(ClassName
.SHOW
)
308 const complete
= () => {
309 const prevHoverState
= this._hoverState
310 this._hoverState
= null
311 this._isTransitioning
= false
313 $(this.element
).trigger(this.constructor.Event
.SHOWN
)
315 if (prevHoverState
=== HoverState
.OUT
) {
316 this._leave(null, this)
320 if (Util
.supportsTransitionEnd() && $(this.tip
).hasClass(ClassName
.FADE
)) {
321 this._isTransitioning
= true
323 .one(Util
.TRANSITION_END
, complete
)
324 .emulateTransitionEnd(Tooltip
._TRANSITION_DURATION
)
333 const tip
= this.getTipElement()
334 const hideEvent
= $.Event(this.constructor.Event
.HIDE
)
335 if (this._isTransitioning
) {
336 throw new Error('Tooltip is transitioning')
338 const complete
= () => {
339 if (this._hoverState
!== HoverState
.SHOW
&& tip
.parentNode
) {
340 tip
.parentNode
.removeChild(tip
)
343 this.element
.removeAttribute('aria-describedby')
344 $(this.element
).trigger(this.constructor.Event
.HIDDEN
)
345 this._isTransitioning
= false
353 $(this.element
).trigger(hideEvent
)
355 if (hideEvent
.isDefaultPrevented()) {
359 $(tip
).removeClass(ClassName
.SHOW
)
361 this._activeTrigger
[Trigger
.CLICK
] = false
362 this._activeTrigger
[Trigger
.FOCUS
] = false
363 this._activeTrigger
[Trigger
.HOVER
] = false
365 if (Util
.supportsTransitionEnd() &&
366 $(this.tip
).hasClass(ClassName
.FADE
)) {
367 this._isTransitioning
= true
369 .one(Util
.TRANSITION_END
, complete
)
370 .emulateTransitionEnd(TRANSITION_DURATION
)
376 this._hoverState
= ''
383 return Boolean(this.getTitle())
387 return this.tip
= this.tip
|| $(this.config
.template
)[0]
391 const $tip
= $(this.getTipElement())
393 this.setElementContent($tip
.find(Selector
.TOOLTIP_INNER
), this.getTitle())
395 $tip
.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`)
400 setElementContent($element
, content
) {
401 const html
= this.config
.html
402 if (typeof content
=== 'object' && (content
.nodeType
|| content
.jquery
)) {
403 // content is a DOM node or a jQuery
405 if (!$(content
).parent().is($element
)) {
406 $element
.empty().append(content
)
409 $element
.text($(content
).text())
412 $element
[html
? 'html' : 'text'](content
)
417 let title
= this.element
.getAttribute('data-original-title')
420 title
= typeof this.config
.title
=== 'function' ?
421 this.config
.title
.call(this.element
) :
430 this._tether
.destroy()
437 _getAttachment(placement
) {
438 return AttachmentMap
[placement
.toUpperCase()]
442 const triggers
= this.config
.trigger
.split(' ')
444 triggers
.forEach((trigger
) => {
445 if (trigger
=== 'click') {
447 this.constructor.Event
.CLICK
,
448 this.config
.selector
,
449 (event
) => this.toggle(event
)
452 } else if (trigger
!== Trigger
.MANUAL
) {
453 const eventIn
= trigger
=== Trigger
.HOVER
?
454 this.constructor.Event
.MOUSEENTER
:
455 this.constructor.Event
.FOCUSIN
456 const eventOut
= trigger
=== Trigger
.HOVER
?
457 this.constructor.Event
.MOUSELEAVE
:
458 this.constructor.Event
.FOCUSOUT
463 this.config
.selector
,
464 (event
) => this._enter(event
)
468 this.config
.selector
,
469 (event
) => this._leave(event
)
473 $(this.element
).closest('.modal').on(
479 if (this.config
.selector
) {
480 this.config
= $.extend({}, this.config
, {
490 const titleType
= typeof this.element
.getAttribute('data-original-title')
491 if (this.element
.getAttribute('title') ||
492 titleType
!== 'string') {
493 this.element
.setAttribute(
494 'data-original-title',
495 this.element
.getAttribute('title') || ''
497 this.element
.setAttribute('title', '')
501 _enter(event
, context
) {
502 const dataKey
= this.constructor.DATA_KEY
504 context
= context
|| $(event
.currentTarget
).data(dataKey
)
507 context
= new this.constructor(
509 this._getDelegateConfig()
511 $(event
.currentTarget
).data(dataKey
, context
)
515 context
._activeTrigger
[
516 event
.type
=== 'focusin' ? Trigger
.FOCUS
: Trigger
.HOVER
520 if ($(context
.getTipElement()).hasClass(ClassName
.SHOW
) ||
521 context
._hoverState
=== HoverState
.SHOW
) {
522 context
._hoverState
= HoverState
.SHOW
526 clearTimeout(context
._timeout
)
528 context
._hoverState
= HoverState
.SHOW
530 if (!context
.config
.delay
|| !context
.config
.delay
.show
) {
535 context
._timeout
= setTimeout(() => {
536 if (context
._hoverState
=== HoverState
.SHOW
) {
539 }, context
.config
.delay
.show
)
542 _leave(event
, context
) {
543 const dataKey
= this.constructor.DATA_KEY
545 context
= context
|| $(event
.currentTarget
).data(dataKey
)
548 context
= new this.constructor(
550 this._getDelegateConfig()
552 $(event
.currentTarget
).data(dataKey
, context
)
556 context
._activeTrigger
[
557 event
.type
=== 'focusout' ? Trigger
.FOCUS
: Trigger
.HOVER
561 if (context
._isWithActiveTrigger()) {
565 clearTimeout(context
._timeout
)
567 context
._hoverState
= HoverState
.OUT
569 if (!context
.config
.delay
|| !context
.config
.delay
.hide
) {
574 context
._timeout
= setTimeout(() => {
575 if (context
._hoverState
=== HoverState
.OUT
) {
578 }, context
.config
.delay
.hide
)
581 _isWithActiveTrigger() {
582 for (const trigger
in this._activeTrigger
) {
583 if (this._activeTrigger
[trigger
]) {
594 this.constructor.Default
,
595 $(this.element
).data(),
599 if (config
.delay
&& typeof config
.delay
=== 'number') {
606 Util
.typeCheckConfig(
609 this.constructor.DefaultType
615 _getDelegateConfig() {
619 for (const key
in this.config
) {
620 if (this.constructor.Default
[key
] !== this.config
[key
]) {
621 config
[key
] = this.config
[key
]
632 static _jQueryInterface(config
) {
633 return this.each(function () {
634 let data
= $(this).data(DATA_KEY
)
635 const _config
= typeof config
=== 'object' && config
637 if (!data
&& /dispose|hide/.test(config
)) {
642 data
= new Tooltip(this, _config
)
643 $(this).data(DATA_KEY
, data
)
646 if (typeof config
=== 'string') {
647 if (data
[config
] === undefined) {
648 throw new Error(`No method named "${config}"`)
659 * ------------------------------------------------------------------------
661 * ------------------------------------------------------------------------
664 $.fn
[NAME
] = Tooltip
._jQueryInterface
665 $.fn
[NAME
].Constructor
= Tooltip
666 $.fn
[NAME
].noConflict = function () {
667 $.fn
[NAME
] = JQUERY_NO_CONFLICT
668 return Tooltip
._jQueryInterface
675 export default Tooltip