bootstrap-vue
Version:
BootstrapVue, with over 40 plugins and more than 75 custom components, provides one of the most comprehensive implementations of Bootstrap v4 components and grid system for Vue.js. With extensive and automated WAI-ARIA accessibility markup.
1,170 lines (965 loc) • 34 kB
JavaScript
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
import Popper from 'popper.js';
import BvEvent from './bv-event.class';
import noop from './noop';
import { from as arrayFrom } from './array';
import { closest, select, isVisible, isDisabled, getCS, addClass, removeClass, hasClass, setAttr, removeAttr, getAttr, eventOn, eventOff } from './dom';
import { isFunction, isNull, isNumber, isObject, isString, isUndefined } from './inspect';
var NAME = 'tooltip';
var CLASS_PREFIX = 'bs-tooltip';
var BS_CLASS_PREFIX_REGEX = new RegExp("\\b".concat(CLASS_PREFIX, "\\S+"), 'g');
var TRANSITION_DURATION = 150; // Modal $root hidden event
var MODAL_CLOSE_EVENT = 'bv::modal::hidden'; // Modal container for appending tooltip/popover
var MODAL_CLASS = '.modal-content';
var AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
RIGHT: 'right',
BOTTOM: 'bottom',
LEFT: 'left',
TOPLEFT: 'top',
TOPRIGHT: 'top',
RIGHTTOP: 'right',
RIGHTBOTTOM: 'right',
BOTTOMLEFT: 'bottom',
BOTTOMRIGHT: 'bottom',
LEFTTOP: 'left',
LEFTBOTTOM: 'left'
};
var OffsetMap = {
AUTO: 0,
TOPLEFT: -1,
TOP: 0,
TOPRIGHT: +1,
RIGHTTOP: -1,
RIGHT: 0,
RIGHTBOTTOM: +1,
BOTTOMLEFT: -1,
BOTTOM: 0,
BOTTOMRIGHT: +1,
LEFTTOP: -1,
LEFT: 0,
LEFTBOTTOM: +1
};
var HoverState = {
SHOW: 'show',
OUT: 'out'
};
var ClassName = {
FADE: 'fade',
SHOW: 'show'
};
var Selector = {
TOOLTIP: '.tooltip',
TOOLTIP_INNER: '.tooltip-inner',
ARROW: '.arrow'
};
var Defaults = {
animation: true,
template: '<div class="tooltip" role="tooltip">' + '<div class="arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
trigger: 'hover focus',
title: '',
delay: 0,
html: false,
placement: 'top',
offset: 0,
arrowPadding: 6,
container: false,
fallbackPlacement: 'flip',
callbacks: {},
boundary: 'scrollParent',
boundaryPadding: 5 // Transition event names
};
var TransitionEndEvents = {
WebkitTransition: ['webkitTransitionEnd'],
MozTransition: ['transitionend'],
OTransition: ['otransitionend', 'oTransitionEnd'],
transition: ['transitionend'] // Options for Native Event Listeners (since we never call preventDefault)
};
var EvtOpts = {
passive: true,
capture: false // Client-side tip ID counter for aria-describedby attribute
// Each tooltip requires a unique client side ID
};
var NEXTID = 1;
/* istanbul ignore next */
var generateId = function generateId(name) {
return "__BV_".concat(name, "_").concat(NEXTID++, "__");
};
/*
* ToolTip class definition
*/
var ToolTip =
/*#__PURE__*/
function () {
// Main constructor
function ToolTip(element, config, $root) {
_classCallCheck(this, ToolTip);
// New tooltip object
this.$isEnabled = true;
this.$fadeTimeout = null;
this.$hoverTimeout = null;
this.$visibleInterval = null;
this.$hoverState = '';
this.$activeTrigger = {};
this.$popper = null;
this.$element = element;
this.$tip = null;
this.$id = generateId(this.constructor.NAME);
this.$root = $root || null;
this.$routeWatcher = null; // We use a bound version of the following handlers for root/modal
// listeners to maintain the 'this' context
this.$forceHide = this.forceHide.bind(this);
this.$doHide = this.doHide.bind(this);
this.$doShow = this.doShow.bind(this);
this.$doDisable = this.doDisable.bind(this);
this.$doEnable = this.doEnable.bind(this);
this._noop = noop.bind(this); // Set the configuration
this.updateConfig(config);
} // NOTE: Overridden by PopOver class
_createClass(ToolTip, [{
key: "updateConfig",
// Update config
value: function updateConfig(config) {
// Merge config into defaults. We use "this" here because PopOver overrides Default
var updatedConfig = _objectSpread({}, this.constructor.Default, config); // Sanitize delay
if (config.delay && isNumber(config.delay)) {
/* istanbul ignore next */
updatedConfig.delay = {
show: config.delay,
hide: config.delay
};
} // Title for tooltip and popover
if (config.title && isNumber(config.title)) {
/* istanbul ignore next */
updatedConfig.title = config.title.toString();
} // Content only for popover
if (config.content && isNumber(config.content)) {
/* istanbul ignore next */
updatedConfig.content = config.content.toString();
} // Hide element original title if needed
this.fixTitle(); // Update the config
this.$config = updatedConfig; // Stop/Restart listening
this.unListen();
this.listen();
} // Destroy this instance
}, {
key: "destroy",
value: function destroy() {
// Stop listening to trigger events
this.unListen(); // Disable while open listeners/watchers
this.setWhileOpenListeners(false); // Clear any timeouts
clearTimeout(this.$hoverTimeout);
this.$hoverTimeout = null;
clearTimeout(this.$fadeTimeout);
this.$fadeTimeout = null; // Remove popper
if (this.$popper) {
this.$popper.destroy();
}
this.$popper = null; // Remove tip from document
if (this.$tip && this.$tip.parentElement) {
this.$tip.parentElement.removeChild(this.$tip);
}
this.$tip = null; // Null out other properties
this.$id = null;
this.$isEnabled = null;
this.$root = null;
this.$element = null;
this.$config = null;
this.$hoverState = null;
this.$activeTrigger = null;
this.$forceHide = null;
this.$doHide = null;
this.$doShow = null;
this.$doDisable = null;
this.$doEnable = null;
}
}, {
key: "enable",
value: function enable() {
// Create a non-cancelable BvEvent
var enabledEvt = new BvEvent('enabled', {
cancelable: false,
target: this.$element,
relatedTarget: null
});
this.$isEnabled = true;
this.emitEvent(enabledEvt);
}
}, {
key: "disable",
value: function disable() {
// Create a non-cancelable BvEvent
var disabledEvt = new BvEvent('disabled', {
cancelable: false,
target: this.$element,
relatedTarget: null
});
this.$isEnabled = false;
this.emitEvent(disabledEvt);
} // Click toggler
}, {
key: "toggle",
value: function toggle(event) {
if (!this.$isEnabled) {
/* istanbul ignore next */
return;
}
/* istanbul ignore else */
if (event) {
this.$activeTrigger.click = !this.$activeTrigger.click;
if (this.isWithActiveTrigger()) {
this.enter(null);
} else {
this.leave(null);
}
} else {
if (hasClass(this.getTipElement(), ClassName.SHOW)) {
this.leave(null);
} else {
this.enter(null);
}
}
} // Show tooltip
}, {
key: "show",
value: function show() {
var _this = this;
if (!document.body.contains(this.$element) || !isVisible(this.$element)) {
// If trigger element isn't in the DOM or is not visible
return;
} // Build tooltip element (also sets this.$tip)
var tip = this.getTipElement();
this.fixTitle();
this.setContent(tip);
if (!this.isWithContent(tip)) {
// If no content, don't bother showing
/* istanbul ignore next */
this.$tip = null;
/* istanbul ignore next */
return;
} // Set ID on tip and aria-describedby on element
setAttr(tip, 'id', this.$id);
this.addAriaDescribedby(); // Set animation on or off
if (this.$config.animation) {
addClass(tip, ClassName.FADE);
} else {
removeClass(tip, ClassName.FADE);
}
var placement = this.getPlacement();
var attachment = this.constructor.getAttachment(placement);
this.addAttachmentClass(attachment); // Create a cancelable BvEvent
var showEvt = new BvEvent('show', {
cancelable: true,
target: this.$element,
relatedTarget: tip
});
this.emitEvent(showEvt);
if (showEvt.defaultPrevented) {
// Don't show if event cancelled
this.$tip = null;
return;
} // Insert tooltip if needed
var container = this.getContainer();
if (!document.body.contains(tip)) {
container.appendChild(tip);
} // Refresh popper
this.removePopper();
this.$popper = new Popper(this.$element, tip, this.getPopperConfig(placement, tip)); // Transitionend callback
var complete = function complete() {
if (_this.$config.animation) {
_this.fixTransition(tip);
}
var prevHoverState = _this.$hoverState;
_this.$hoverState = null;
if (prevHoverState === HoverState.OUT) {
_this.leave(null);
} // Create a non-cancelable BvEvent
var shownEvt = new BvEvent('shown', {
cancelable: false,
target: _this.$element,
relatedTarget: tip
});
_this.emitEvent(shownEvt);
}; // Enable while open listeners/watchers
this.setWhileOpenListeners(true); // Show tip
addClass(tip, ClassName.SHOW); // Start the transition/animation
this.transitionOnce(tip, complete);
} // Handler for periodic visibility check
}, {
key: "visibleCheck",
value: function visibleCheck(on) {
var _this2 = this;
clearInterval(this.$visibleInterval);
this.$visibleInterval = null;
if (on) {
this.$visibleInterval = setInterval(function () {
var tip = _this2.$tip;
if (tip && !isVisible(_this2.$element) && hasClass(tip, ClassName.SHOW)) {
// Element is no longer visible, so force-hide the tooltip
_this2.forceHide();
}
}, 100);
}
}
}, {
key: "setWhileOpenListeners",
value: function setWhileOpenListeners(on) {
// Modal close events
this.setModalListener(on); // Periodic $element visibility check
// For handling when tip is in <keepalive>, tabs, carousel, etc
this.visibleCheck(on); // Route change events
this.setRouteWatcher(on); // On-touch start listeners
this.setOnTouchStartListener(on);
if (on && /(focus|blur)/.test(this.$config.trigger)) {
// If focus moves between trigger element and tip container, don't close
eventOn(this.$tip, 'focusout', this, EvtOpts);
} else {
eventOff(this.$tip, 'focusout', this, EvtOpts);
}
} // Force hide of tip (internal method)
}, {
key: "forceHide",
value: function forceHide() {
if (!this.$tip || !hasClass(this.$tip, ClassName.SHOW)) {
/* istanbul ignore next */
return;
} // Disable while open listeners/watchers
this.setWhileOpenListeners(false); // Clear any hover enter/leave event
clearTimeout(this.$hoverTimeout);
this.$hoverTimeout = null;
this.$hoverState = ''; // Hide the tip
this.hide(null, true);
} // Hide tooltip
}, {
key: "hide",
value: function hide(callback, force) {
var _this3 = this;
var tip = this.$tip;
if (!tip) {
/* istanbul ignore next */
return;
} // Create a cancelable BvEvent
var hideEvt = new BvEvent('hide', {
// We disable cancelling if force is true
cancelable: !force,
target: this.$element,
relatedTarget: tip
});
this.emitEvent(hideEvt);
if (hideEvt.defaultPrevented) {
// Don't hide if event cancelled
return;
} // Transitionend callback
var complete = function complete() {
if (_this3.$hoverState !== HoverState.SHOW && tip.parentNode) {
// Remove tip from DOM, and force recompile on next show
tip.parentNode.removeChild(tip);
_this3.removeAriaDescribedby();
_this3.removePopper();
_this3.$tip = null;
}
if (callback) {
callback();
} // Create a non-cancelable BvEvent
var hiddenEvt = new BvEvent('hidden', {
cancelable: false,
target: _this3.$element,
relatedTarget: null
});
_this3.emitEvent(hiddenEvt);
}; // Disable while open listeners/watchers
this.setWhileOpenListeners(false); // If forced close, disable animation
if (force) {
removeClass(tip, ClassName.FADE);
} // Hide tip
removeClass(tip, ClassName.SHOW);
this.$activeTrigger.click = false;
this.$activeTrigger.focus = false;
this.$activeTrigger.hover = false; // Start the hide transition
this.transitionOnce(tip, complete);
this.$hoverState = '';
}
}, {
key: "emitEvent",
value: function emitEvent(evt) {
var evtName = evt.type;
if (this.$root && this.$root.$emit) {
// Emit an event on $root
this.$root.$emit("bv::".concat(this.constructor.NAME, "::").concat(evtName), evt);
}
var callbacks = this.$config.callbacks || {};
if (isFunction(callbacks[evtName])) {
callbacks[evtName](evt);
}
}
}, {
key: "getContainer",
value: function getContainer() {
var container = this.$config.container;
var body = document.body; // If we are in a modal, we append to the modal instead of body,
// unless a container is specified
return container === false ? closest(MODAL_CLASS, this.$element) || body : select(container, body) || body;
} // Will be overridden by PopOver if needed
}, {
key: "addAriaDescribedby",
value: function addAriaDescribedby() {
// Add aria-describedby on trigger element, without removing any other IDs
var desc = getAttr(this.$element, 'aria-describedby') || '';
desc = desc.split(/\s+/).concat(this.$id).join(' ').trim();
setAttr(this.$element, 'aria-describedby', desc);
} // Will be overridden by PopOver if needed
}, {
key: "removeAriaDescribedby",
value: function removeAriaDescribedby() {
var _this4 = this;
var desc = getAttr(this.$element, 'aria-describedby') || '';
desc = desc.split(/\s+/).filter(function (d) {
return d !== _this4.$id;
}).join(' ').trim();
if (desc) {
/* istanbul ignore next */
setAttr(this.$element, 'aria-describedby', desc);
} else {
removeAttr(this.$element, 'aria-describedby');
}
}
}, {
key: "removePopper",
value: function removePopper() {
if (this.$popper) {
this.$popper.destroy();
}
this.$popper = null;
}
}, {
key: "transitionOnce",
value: function transitionOnce(tip, complete) {
var _this5 = this;
var transEvents = this.getTransitionEndEvents();
var called = false;
clearTimeout(this.$fadeTimeout);
this.$fadeTimeout = null;
var fnOnce = function fnOnce() {
if (called) {
/* istanbul ignore next */
return;
}
called = true;
clearTimeout(_this5.$fadeTimeout);
_this5.$fadeTimeout = null;
transEvents.forEach(function (evtName) {
eventOff(tip, evtName, fnOnce, EvtOpts);
}); // Call complete callback
complete();
};
if (hasClass(tip, ClassName.FADE)) {
transEvents.forEach(function (evtName) {
eventOn(tip, evtName, fnOnce, EvtOpts);
}); // Fallback to setTimeout()
this.$fadeTimeout = setTimeout(fnOnce, TRANSITION_DURATION);
} else {
fnOnce();
}
} // What transitionend event(s) to use? (returns array of event names)
}, {
key: "getTransitionEndEvents",
value: function getTransitionEndEvents() {
for (var name in TransitionEndEvents) {
if (!isUndefined(this.$element.style[name])) {
return TransitionEndEvents[name];
}
} // Fallback
/* istanbul ignore next */
return [];
}
/* istanbul ignore next */
}, {
key: "update",
value: function update() {
if (!isNull(this.$popper)) {
this.$popper.scheduleUpdate();
}
} // NOTE: Overridden by PopOver class
}, {
key: "isWithContent",
value: function isWithContent(tip) {
tip = tip || this.$tip;
if (!tip) {
/* istanbul ignore next */
return false;
}
return Boolean((select(Selector.TOOLTIP_INNER, tip) || {}).innerHTML);
} // NOTE: Overridden by PopOver class
}, {
key: "addAttachmentClass",
value: function addAttachmentClass(attachment) {
addClass(this.getTipElement(), "".concat(CLASS_PREFIX, "-").concat(attachment));
}
}, {
key: "getTipElement",
value: function getTipElement() {
if (!this.$tip) {
// Try and compile user supplied template, or fallback to default template
this.$tip = this.compileTemplate(this.$config.template) || this.compileTemplate(this.constructor.Default.template);
} // Add tab index so tip can be focused, and to allow it to be
// set as relatedTarget in focusin/out events
this.$tip.tabIndex = -1;
return this.$tip;
}
}, {
key: "compileTemplate",
value: function compileTemplate(html) {
if (!html || !isString(html)) {
/* istanbul ignore next */
return null;
}
var div = document.createElement('div');
div.innerHTML = html.trim();
var node = div.firstElementChild ? div.removeChild(div.firstElementChild) : null;
div = null;
return node;
} // NOTE: Overridden by PopOver class
}, {
key: "setContent",
value: function setContent(tip) {
this.setElementContent(select(Selector.TOOLTIP_INNER, tip), this.getTitle());
removeClass(tip, ClassName.FADE);
removeClass(tip, ClassName.SHOW);
}
}, {
key: "setElementContent",
value: function setElementContent(container, content) {
if (!container) {
// If container element doesn't exist, just return
/* istanbul ignore next */
return;
}
var allowHtml = this.$config.html;
if (isObject(content) && content.nodeType) {
// Content is a DOM node
if (allowHtml) {
if (content.parentElement !== container) {
container.innerHTML = '';
container.appendChild(content);
}
} else {
/* istanbul ignore next */
container.innerText = content.innerText;
}
} else {
// We have a plain HTML string or Text
container[allowHtml ? 'innerHTML' : 'innerText'] = content;
}
} // NOTE: Overridden by PopOver class
}, {
key: "getTitle",
value: function getTitle() {
var title = this.$config.title || '';
if (isFunction(title)) {
// Call the function to get the title value
/* istanbul ignore next */
title = title(this.$element);
}
if (isObject(title) && title.nodeType && !title.innerHTML.trim()) {
// We have a DOM node, but without inner content,
// so just return empty string
/* istanbul ignore next */
title = '';
}
if (isString(title)) {
title = title.trim();
}
if (!title) {
// If an explicit title is not given, try element's title attributes
title = getAttr(this.$element, 'title') || getAttr(this.$element, 'data-original-title') || '';
title = title.trim();
}
return title;
}
}, {
key: "listen",
value: function listen() {
var _this6 = this;
var triggers = this.$config.trigger.trim().split(/\s+/);
var el = this.$element; // Listen for global show/hide events
this.setRootListener(true); // Using 'this' as the handler will get automatically directed to
// this.handleEvent and maintain our binding to 'this'
triggers.forEach(function (trigger) {
if (trigger === 'click') {
eventOn(el, 'click', _this6, EvtOpts);
} else if (trigger === 'focus') {
eventOn(el, 'focusin', _this6, EvtOpts);
eventOn(el, 'focusout', _this6, EvtOpts);
} else if (trigger === 'blur') {
// Used to close $tip when element looses focus
eventOn(el, 'focusout', _this6, EvtOpts);
} else if (trigger === 'hover') {
eventOn(el, 'mouseenter', _this6, EvtOpts);
eventOn(el, 'mouseleave', _this6, EvtOpts);
}
}, this);
}
}, {
key: "unListen",
value: function unListen() {
var _this7 = this;
var events = ['click', 'focusin', 'focusout', 'mouseenter', 'mouseleave']; // Using "this" as the handler will get automatically directed to this.handleEvent
events.forEach(function (evt) {
eventOff(_this7.$element, evt, _this7, EvtOpts);
}, this); // Stop listening for global show/hide/enable/disable events
this.setRootListener(false);
}
}, {
key: "handleEvent",
value: function handleEvent(e) {
// This special method allows us to use "this" as the event handlers
if (isDisabled(this.$element)) {
// If disabled, don't do anything. Note: If tip is shown before element gets
// disabled, then tip not close until no longer disabled or forcefully closed.
/* istanbul ignore next */
return;
}
if (!this.$isEnabled) {
// If not enable
return;
}
var type = e.type;
var target = e.target;
var relatedTarget = e.relatedTarget;
var $element = this.$element;
var $tip = this.$tip;
if (type === 'click') {
this.toggle(e);
} else if (type === 'focusin' || type === 'mouseenter') {
this.enter(e);
} else if (type === 'focusout') {
// target is the element which is loosing focus
// and relatedTarget is the element gaining focus
if ($tip && $element && $element.contains(target) && $tip.contains(relatedTarget)) {
// If focus moves from $element to $tip, don't trigger a leave
/* istanbul ignore next */
return;
}
if ($tip && $element && $tip.contains(target) && $element.contains(relatedTarget)) {
// If focus moves from $tip to $element, don't trigger a leave
/* istanbul ignore next */
return;
}
/* istanbul ignore next: difficult to test */
if ($tip && $tip.contains(target) && $tip.contains(relatedTarget)) {
// If focus moves within $tip, don't trigger a leave
return;
}
/* istanbul ignore next: difficult to test */
if ($element && $element.contains(target) && $element.contains(relatedTarget)) {
// If focus moves within $element, don't trigger a leave
return;
} // Otherwise trigger a leave
this.leave(e);
} else if (type === 'mouseleave') {
this.leave(e);
}
}
/* istanbul ignore next */
}, {
key: "setRouteWatcher",
value: function setRouteWatcher(on) {
var _this8 = this;
if (on) {
this.setRouteWatcher(false);
if (this.$root && Boolean(this.$root.$route)) {
this.$routeWatcher = this.$root.$watch('$route', function (newVal, oldVal) {
if (newVal === oldVal) {
return;
} // If route has changed, we force hide the tooltip/popover
_this8.forceHide();
});
}
} else {
if (this.$routeWatcher) {
// Cancel the route watcher by calling the stored reference
this.$routeWatcher();
this.$routeWatcher = null;
}
}
}
/* istanbul ignore next */
}, {
key: "setModalListener",
value: function setModalListener(on) {
var modal = closest(MODAL_CLASS, this.$element);
if (!modal) {
// If we are not in a modal, don't worry. be happy
return;
} // We can listen for modal hidden events on $root
if (this.$root) {
this.$root[on ? '$on' : '$off'](MODAL_CLOSE_EVENT, this.$forceHide);
}
}
}, {
key: "setRootListener",
value: function setRootListener(on) {
// Listen for global 'bv::{hide|show}::{tooltip|popover}' hide request event
if (this.$root) {
this.$root[on ? '$on' : '$off']("bv::hide::".concat(this.constructor.NAME), this.$doHide);
this.$root[on ? '$on' : '$off']("bv::show::".concat(this.constructor.NAME), this.$doShow);
this.$root[on ? '$on' : '$off']("bv::disable::".concat(this.constructor.NAME), this.$doDisable);
this.$root[on ? '$on' : '$off']("bv::enable::".concat(this.constructor.NAME), this.$doEnable);
}
}
}, {
key: "doHide",
value: function doHide(id) {
// Programmatically hide tooltip or popover
if (!id) {
// Close all tooltips or popovers
this.forceHide();
} else if (this.$element && this.$element.id && this.$element.id === id) {
// Close this specific tooltip or popover
this.hide();
}
}
}, {
key: "doShow",
value: function doShow(id) {
// Programmatically show tooltip or popover
if (!id) {
// Open all tooltips or popovers
this.show();
} else if (id && this.$element && this.$element.id && this.$element.id === id) {
// Show this specific tooltip or popover
this.show();
}
}
}, {
key: "doDisable",
value: function doDisable(id) {
// Programmatically disable tooltip or popover
if (!id) {
// Disable all tooltips or popovers
this.disable();
} else if (this.$element && this.$element.id && this.$element.id === id) {
// Disable this specific tooltip or popover
this.disable();
}
}
}, {
key: "doEnable",
value: function doEnable(id) {
// Programmatically enable tooltip or popover
if (!id) {
// Enable all tooltips or popovers
this.enable();
} else if (this.$element && this.$element.id && this.$element.id === id) {
// Enable this specific tooltip or popover
this.enable();
}
}
}, {
key: "setOnTouchStartListener",
value: function setOnTouchStartListener(on) {
var _this9 = this;
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children
// Only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) {
/* istanbul ignore next: JSDOM does not support 'ontouchstart' event */
arrayFrom(document.body.children).forEach(function (el) {
if (on) {
eventOn(el, 'mouseover', _this9._noop);
} else {
eventOff(el, 'mouseover', _this9._noop);
}
});
}
}
}, {
key: "fixTitle",
value: function fixTitle() {
var el = this.$element;
if (getAttr(el, 'title') || !isString(getAttr(el, 'data-original-title'))) {
setAttr(el, 'data-original-title', getAttr(el, 'title') || '');
setAttr(el, 'title', '');
}
} // Enter handler
}, {
key: "enter",
value: function enter(e) {
var _this10 = this;
if (e) {
this.$activeTrigger[e.type === 'focusin' ? 'focus' : 'hover'] = true;
}
if (hasClass(this.getTipElement(), ClassName.SHOW) || this.$hoverState === HoverState.SHOW) {
this.$hoverState = HoverState.SHOW;
return;
}
clearTimeout(this.$hoverTimeout);
this.$hoverState = HoverState.SHOW;
if (!this.$config.delay || !this.$config.delay.show) {
this.show();
return;
}
this.$hoverTimeout = setTimeout(function () {
if (_this10.$hoverState === HoverState.SHOW) {
_this10.show();
}
}, this.$config.delay.show);
} // Leave handler
}, {
key: "leave",
value: function leave(e) {
var _this11 = this;
if (e) {
this.$activeTrigger[e.type === 'focusout' ? 'focus' : 'hover'] = false;
if (e.type === 'focusout' && /blur/.test(this.$config.trigger)) {
// Special case for `blur`: we clear out the other triggers
this.$activeTrigger.click = false;
this.$activeTrigger.hover = false;
}
}
if (this.isWithActiveTrigger()) {
return;
}
clearTimeout(this.$hoverTimeout);
this.$hoverState = HoverState.OUT;
if (!this.$config.delay || !this.$config.delay.hide) {
this.hide();
return;
}
this.$hoverTimeout = setTimeout(function () {
if (_this11.$hoverState === HoverState.OUT) {
_this11.hide();
}
}, this.$config.delay.hide);
}
}, {
key: "getPopperConfig",
value: function getPopperConfig(placement, tip) {
var _this12 = this;
return {
placement: this.constructor.getAttachment(placement),
modifiers: {
offset: {
offset: this.getOffset(placement, tip)
},
flip: {
behavior: this.$config.fallbackPlacement
},
arrow: {
element: '.arrow'
},
preventOverflow: {
padding: this.$config.boundaryPadding,
boundariesElement: this.$config.boundary
}
},
onCreate: function onCreate(data) {
// Handle flipping arrow classes
/* istanbul ignore next */
if (data.originalPlacement !== data.placement) {
_this12.handlePopperPlacementChange(data);
}
},
onUpdate: function onUpdate(data) {
// Handle flipping arrow classes
/* istanbul ignore next */
_this12.handlePopperPlacementChange(data);
}
};
}
/* istanbul ignore next */
}, {
key: "getOffset",
value: function getOffset(placement, tip) {
if (!this.$config.offset) {
var arrow = select(Selector.ARROW, tip);
var arrowOffset = parseFloat(getCS(arrow).width) + parseFloat(this.$config.arrowPadding);
switch (OffsetMap[placement.toUpperCase()]) {
case +1:
return "+50%p - ".concat(arrowOffset, "px");
case -1:
return "-50%p + ".concat(arrowOffset, "px");
default:
return 0;
}
}
return this.$config.offset;
}
}, {
key: "getPlacement",
value: function getPlacement() {
var placement = this.$config.placement;
if (isFunction(placement)) {
/* istanbul ignore next */
return placement.call(this, this.$tip, this.$element);
}
return placement;
}
}, {
key: "isWithActiveTrigger",
value: function isWithActiveTrigger() {
for (var trigger in this.$activeTrigger) {
if (this.$activeTrigger[trigger]) {
return true;
}
}
return false;
} // NOTE: Overridden by PopOver class
/* istanbul ignore next */
}, {
key: "cleanTipClass",
value: function cleanTipClass() {
var tip = this.getTipElement();
var tabClass = tip.className.match(BS_CLASS_PREFIX_REGEX);
if (!isNull(tabClass) && tabClass.length > 0) {
tabClass.forEach(function (cls) {
removeClass(tip, cls);
});
}
}
/* istanbul ignore next */
}, {
key: "handlePopperPlacementChange",
value: function handlePopperPlacementChange(data) {
this.cleanTipClass();
this.addAttachmentClass(this.constructor.getAttachment(data.placement));
}
/* istanbul ignore next */
}, {
key: "fixTransition",
value: function fixTransition(tip) {
var initConfigAnimation = this.$config.animation || false;
if (!isNull(getAttr(tip, 'x-placement'))) {
return;
}
removeClass(tip, ClassName.FADE);
this.$config.animation = false;
this.hide();
this.show();
this.$config.animation = initConfigAnimation;
}
}], [{
key: "getAttachment",
value: function getAttachment(placement) {
return AttachmentMap[placement.toUpperCase()];
}
}, {
key: "Default",
get: function get() {
return Defaults;
} // NOTE: Overridden by PopOver class
}, {
key: "NAME",
get: function get() {
return NAME;
}
}]);
return ToolTip;
}();
export default ToolTip;