@cling-se/v-tooltip-root-node
Version:
vTooltip with shadow dom support
1,499 lines (1,445 loc) • 122 kB
JavaScript
import Popper from 'popper.js';
import { ResizeObserver } from 'vue-resize';
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
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, _toPropertyKey(descriptor.key), descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _toPrimitive(input, hint) {
if (typeof input !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (typeof res !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, "string");
return typeof key === "symbol" ? key : String(key);
}
var SVGAnimatedString = function SVGAnimatedString() {};
if (typeof window !== 'undefined') {
SVGAnimatedString = window.SVGAnimatedString;
}
function convertToArray(value) {
if (typeof value === 'string') {
value = value.split(' ');
}
return value;
}
/**
* Add classes to an element.
* This method checks to ensure that the classes don't already exist before adding them.
* It uses el.className rather than classList in order to be IE friendly.
* @param {object} el - The element to add the classes to.
* @param {classes} string - List of space separated classes to be added to the element.
*/
function addClasses(el, classes) {
var newClasses = convertToArray(classes);
var classList;
if (el.className instanceof SVGAnimatedString) {
classList = convertToArray(el.className.baseVal);
} else {
classList = convertToArray(el.className);
}
newClasses.forEach(function (newClass) {
if (classList.indexOf(newClass) === -1) {
classList.push(newClass);
}
});
if (el instanceof SVGElement) {
el.setAttribute('class', classList.join(' '));
} else {
el.className = classList.join(' ');
}
}
/**
* Remove classes from an element.
* It uses el.className rather than classList in order to be IE friendly.
* @export
* @param {any} el The element to remove the classes from.
* @param {any} classes List of space separated classes to be removed from the element.
*/
function removeClasses(el, classes) {
var newClasses = convertToArray(classes);
var classList;
if (el.className instanceof SVGAnimatedString) {
classList = convertToArray(el.className.baseVal);
} else {
classList = convertToArray(el.className);
}
newClasses.forEach(function (newClass) {
var index = classList.indexOf(newClass);
if (index !== -1) {
classList.splice(index, 1);
}
});
if (el instanceof SVGElement) {
el.setAttribute('class', classList.join(' '));
} else {
el.className = classList.join(' ');
}
}
var supportsPassive = false;
if (typeof window !== 'undefined') {
supportsPassive = false;
try {
var opts = Object.defineProperty({}, 'passive', {
get: function get() {
supportsPassive = true;
}
});
window.addEventListener('test', null, opts);
} catch (e) {}
}
var DEFAULT_OPTIONS = {
container: false,
delay: 0,
html: false,
placement: 'top',
title: '',
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
trigger: 'hover focus',
offset: 0
};
var openTooltips = [];
var Tooltip = /*#__PURE__*/function () {
/**
* Create a new Tooltip.js instance
* @class Tooltip
* @param {HTMLElement} reference - The DOM node used as reference of the tooltip (it can be a jQuery element).
* @param {Object} options
* @param {String} options.placement=bottom
* Placement of the popper accepted values: `top(-start, -end), right(-start, -end), bottom(-start, -end),
* left(-start, -end)`
* @param {HTMLElement|String|false} options.container=false - Append the tooltip to a specific element.
* @param {Number|Object} options.delay=0
* Delay showing and hiding the tooltip (ms) - does not apply to manual trigger type.
* If a number is supplied, delay is applied to both hide/show.
* Object structure is: `{ show: 500, hide: 100 }`
* @param {Boolean} options.html=false - Insert HTML into the tooltip. If false, the content will inserted with `innerText`.
* @param {String|PlacementFunction} options.placement='top' - One of the allowed placements, or a function returning one of them.
* @param {String} [options.template='<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>']
* Base HTML to used when creating the tooltip.
* The tooltip's `title` will be injected into the `.tooltip-inner` or `.tooltip__inner`.
* `.tooltip-arrow` or `.tooltip__arrow` will become the tooltip's arrow.
* The outermost wrapper element should have the `.tooltip` class.
* @param {String|HTMLElement|TitleFunction} options.title='' - Default title value if `title` attribute isn't present.
* @param {String} [options.trigger='hover focus']
* How tooltip is triggered - click, hover, focus, manual.
* You may pass multiple triggers; separate them with a space. `manual` cannot be combined with any other trigger.
* @param {HTMLElement} options.boundariesElement
* The element used as boundaries for the tooltip. For more information refer to Popper.js'
* [boundariesElement docs](https://popper.js.org/popper-documentation.html)
* @param {Number|String} options.offset=0 - Offset of the tooltip relative to its reference. For more information refer to Popper.js'
* [offset docs](https://popper.js.org/popper-documentation.html)
* @param {Object} options.popperOptions={} - Popper options, will be passed directly to popper instance. For more information refer to Popper.js'
* [options docs](https://popper.js.org/popper-documentation.html)
* @return {Object} instance - The generated tooltip instance
*/
function Tooltip(_reference, _options) {
var _this = this;
_classCallCheck(this, Tooltip);
//
// Private methods
//
_defineProperty(this, "_events", []);
_defineProperty(this, "_setTooltipNodeEvent", function (evt, reference, delay, options) {
var relatedreference = evt.relatedreference || evt.toElement || evt.relatedTarget;
var callback = function callback(evt2) {
var relatedreference2 = evt2.relatedreference || evt2.toElement || evt2.relatedTarget;
// Remove event listener after call
_this._tooltipNode.removeEventListener(evt.type, callback);
// If the new reference is not the reference element
if (!reference.contains(relatedreference2)) {
// Schedule to hide tooltip
_this._scheduleHide(reference, options.delay, options, evt2);
}
};
if (_this._tooltipNode.contains(relatedreference)) {
// listen to mouseleave on the tooltip element to be able to hide the tooltip
_this._tooltipNode.addEventListener(evt.type, callback);
return true;
}
return false;
});
// apply user options over default ones
_options = _objectSpread2(_objectSpread2({}, DEFAULT_OPTIONS), _options);
_reference.jquery && (_reference = _reference[0]);
this.show = this.show.bind(this);
this.hide = this.hide.bind(this);
// cache reference and options
this.reference = _reference;
this.options = _options;
// set initial state
this._isOpen = false;
this._init();
}
//
// Public methods
//
/**
* Reveals an element's tooltip. This is considered a "manual" triggering of the tooltip.
* Tooltips with zero-length titles are never displayed.
* @method Tooltip#show
* @memberof Tooltip
*/
_createClass(Tooltip, [{
key: "show",
value: function show() {
this._show(this.reference, this.options);
}
/**
* Hides an element’s tooltip. This is considered a “manual” triggering of the tooltip.
* @method Tooltip#hide
* @memberof Tooltip
*/
}, {
key: "hide",
value: function hide() {
this._hide();
}
/**
* Hides and destroys an element’s tooltip.
* @method Tooltip#dispose
* @memberof Tooltip
*/
}, {
key: "dispose",
value: function dispose() {
this._dispose();
}
/**
* Toggles an element’s tooltip. This is considered a “manual” triggering of the tooltip.
* @method Tooltip#toggle
* @memberof Tooltip
*/
}, {
key: "toggle",
value: function toggle() {
if (this._isOpen) {
return this.hide();
} else {
return this.show();
}
}
}, {
key: "setClasses",
value: function setClasses(classes) {
this._classes = classes;
}
}, {
key: "setContent",
value: function setContent(content) {
this.options.title = content;
if (this._tooltipNode) {
this._setContent(content, this.options);
}
}
}, {
key: "setOptions",
value: function setOptions(options) {
var classesUpdated = false;
var classes = options && options.classes || directive.options.defaultClass;
if (this._classes !== classes) {
this.setClasses(classes);
classesUpdated = true;
}
options = getOptions(options);
var needPopperUpdate = false;
var needRestart = false;
if (this.options.offset !== options.offset || this.options.placement !== options.placement) {
needPopperUpdate = true;
}
if (this.options.template !== options.template || this.options.trigger !== options.trigger || this.options.container !== options.container || classesUpdated) {
needRestart = true;
}
for (var key in options) {
this.options[key] = options[key];
}
if (this._tooltipNode) {
if (needRestart) {
var isOpen = this._isOpen;
this.dispose();
this._init();
if (isOpen) {
this.show();
}
} else if (needPopperUpdate) {
this.popperInstance.update();
}
}
}
}, {
key: "_init",
value: function _init() {
// get events list
var events = typeof this.options.trigger === 'string' ? this.options.trigger.split(' ') : [];
this._isDisposed = false;
this._enableDocumentTouch = events.indexOf('manual') === -1;
events = events.filter(function (trigger) {
return ['click', 'hover', 'focus'].indexOf(trigger) !== -1;
});
// set event listeners
this._setEventListeners(this.reference, events, this.options);
// title attribute
this.$_originalTitle = this.reference.getAttribute('title');
this.reference.removeAttribute('title');
this.reference.setAttribute('data-original-title', this.$_originalTitle);
}
/**
* Creates a new tooltip node
* @memberof Tooltip
* @private
* @param {HTMLElement} reference
* @param {String} template
* @param {String|HTMLElement|TitleFunction} title
* @param {Boolean} allowHtml
* @return {HTMLelement} tooltipNode
*/
}, {
key: "_create",
value: function _create(reference, template) {
// create tooltip element
var tooltipGenerator = window.document.createElement('div');
tooltipGenerator.innerHTML = template.trim();
var tooltipNode = tooltipGenerator.childNodes[0];
// add unique ID to our tooltip (needed for accessibility reasons)
tooltipNode.id = "tooltip_".concat(Math.random().toString(36).substr(2, 10));
// Initially hide the tooltip
// The attribute will be switched in a next frame so
// CSS transitions can play
tooltipNode.setAttribute('aria-hidden', 'true');
if (this.options.autoHide && this.options.trigger.indexOf('hover') !== -1) {
tooltipNode.addEventListener('mouseenter', this.hide);
tooltipNode.addEventListener('click', this.hide);
}
// return the generated tooltip node
return tooltipNode;
}
}, {
key: "_setContent",
value: function _setContent(content, options) {
var _this2 = this;
this.asyncContent = false;
this._applyContent(content, options).then(function () {
_this2.popperInstance.update();
});
}
}, {
key: "_applyContent",
value: function _applyContent(title, options) {
var _this3 = this;
return new Promise(function (resolve, reject) {
var allowHtml = options.html;
var rootNode = _this3._tooltipNode;
if (!rootNode) return;
var titleNode = rootNode.querySelector(_this3.options.innerSelector);
if (title.nodeType === 1) {
// if title is a node, append it only if allowHtml is true
if (allowHtml) {
while (titleNode.firstChild) {
titleNode.removeChild(titleNode.firstChild);
}
titleNode.appendChild(title);
}
} else if (typeof title === 'function') {
// if title is a function, call it and set innerText or innerHtml depending by `allowHtml` value
var result = title();
if (result && typeof result.then === 'function') {
_this3.asyncContent = true;
options.loadingClass && addClasses(rootNode, options.loadingClass);
if (options.loadingContent) {
_this3._applyContent(options.loadingContent, options);
}
result.then(function (asyncResult) {
options.loadingClass && removeClasses(rootNode, options.loadingClass);
return _this3._applyContent(asyncResult, options);
}).then(resolve).catch(reject);
} else {
_this3._applyContent(result, options).then(resolve).catch(reject);
}
return;
} else {
// if it's just a simple text, set innerText or innerHtml depending by `allowHtml` value
allowHtml ? titleNode.innerHTML = title : titleNode.innerText = title;
}
resolve();
});
}
}, {
key: "_show",
value: function _show(reference, options) {
if (options && typeof options.container === 'string') {
var container = this.options.rootNode.querySelector(options.container);
if (!container) return;
}
clearTimeout(this._disposeTimer);
options = Object.assign({}, options);
delete options.offset;
var updateClasses = true;
if (this._tooltipNode) {
addClasses(this._tooltipNode, this._classes);
updateClasses = false;
}
var result = this._ensureShown(reference, options);
if (updateClasses && this._tooltipNode) {
addClasses(this._tooltipNode, this._classes);
}
addClasses(reference, ['v-tooltip-open']);
return result;
}
}, {
key: "_ensureShown",
value: function _ensureShown(reference, options) {
var _this4 = this;
// don't show if it's already visible
if (this._isOpen) {
return this;
}
this._isOpen = true;
openTooltips.push(this);
// if the tooltipNode already exists, just show it
if (this._tooltipNode) {
this._tooltipNode.style.display = '';
this._tooltipNode.setAttribute('aria-hidden', 'false');
this.popperInstance.enableEventListeners();
this.popperInstance.update();
if (this.asyncContent) {
this._setContent(options.title, options);
}
return this;
}
// get title
var title = reference.getAttribute('title') || options.title;
// don't show tooltip if no title is defined
if (!title) {
return this;
}
// create tooltip node
var tooltipNode = this._create(reference, options.template);
this._tooltipNode = tooltipNode;
// Add `aria-describedby` to our reference element for accessibility reasons
reference.setAttribute('aria-describedby', tooltipNode.id);
// append tooltip to container
var container = this._findContainer(options.container, reference);
this._append(tooltipNode, container);
var popperOptions = _objectSpread2(_objectSpread2({}, options.popperOptions), {}, {
placement: options.placement
});
popperOptions.modifiers = _objectSpread2(_objectSpread2({}, popperOptions.modifiers), {}, {
arrow: {
element: this.options.arrowSelector
}
});
if (options.boundariesElement) {
popperOptions.modifiers.preventOverflow = {
boundariesElement: options.boundariesElement
};
}
this.popperInstance = new Popper(reference, tooltipNode, popperOptions);
this._setContent(title, options);
// Fix position
requestAnimationFrame(function () {
if (!_this4._isDisposed && _this4.popperInstance) {
_this4.popperInstance.update();
// Show the tooltip
requestAnimationFrame(function () {
if (!_this4._isDisposed) {
_this4._isOpen && tooltipNode.setAttribute('aria-hidden', 'false');
} else {
_this4.dispose();
}
});
} else {
_this4.dispose();
}
});
return this;
}
}, {
key: "_noLongerOpen",
value: function _noLongerOpen() {
var index = openTooltips.indexOf(this);
if (index !== -1) {
openTooltips.splice(index, 1);
}
}
}, {
key: "_hide",
value: function _hide( /* reference, options */
) {
var _this5 = this;
// don't hide if it's already hidden
if (!this._isOpen) {
return this;
}
this._isOpen = false;
this._noLongerOpen();
// hide tooltipNode
this._tooltipNode.style.display = 'none';
this._tooltipNode.setAttribute('aria-hidden', 'true');
this.popperInstance.disableEventListeners();
clearTimeout(this._disposeTimer);
var disposeTime = directive.options.disposeTimeout;
if (disposeTime !== null) {
this._disposeTimer = setTimeout(function () {
if (_this5._tooltipNode) {
_this5._tooltipNode.removeEventListener('mouseenter', _this5.hide);
_this5._tooltipNode.removeEventListener('click', _this5.hide);
// Don't remove popper instance, just the HTML element
_this5._removeTooltipNode();
}
}, disposeTime);
}
removeClasses(this.reference, ['v-tooltip-open']);
return this;
}
}, {
key: "_removeTooltipNode",
value: function _removeTooltipNode() {
if (!this._tooltipNode) return;
var parentNode = this._tooltipNode.parentNode;
if (parentNode) {
parentNode.removeChild(this._tooltipNode);
this.reference.removeAttribute('aria-describedby');
}
this._tooltipNode = null;
}
}, {
key: "_dispose",
value: function _dispose() {
var _this6 = this;
this._isDisposed = true;
this.reference.removeAttribute('data-original-title');
if (this.$_originalTitle) {
this.reference.setAttribute('title', this.$_originalTitle);
}
// remove event listeners first to prevent any unexpected behaviour
this._events.forEach(function (_ref) {
var func = _ref.func,
event = _ref.event;
_this6.reference.removeEventListener(event, func);
});
this._events = [];
if (this._tooltipNode) {
this._hide();
this._tooltipNode.removeEventListener('mouseenter', this.hide);
this._tooltipNode.removeEventListener('click', this.hide);
// destroy instance
this.popperInstance.destroy();
// destroy tooltipNode if removeOnDestroy is not set, as popperInstance.destroy() already removes the element
if (!this.popperInstance.options.removeOnDestroy) {
this._removeTooltipNode();
}
} else {
this._noLongerOpen();
}
return this;
}
}, {
key: "_findContainer",
value: function _findContainer(container, reference) {
// if container is a query, get the relative element
if (typeof container === 'string') {
container = this.options.rootNode.querySelector(container);
} else if (container === false) {
// if container is `false`, set it to reference parent
container = reference.parentNode;
}
return container;
}
/**
* Append tooltip to container
* @memberof Tooltip
* @private
* @param {HTMLElement} tooltip
* @param {HTMLElement|String|false} container
*/
}, {
key: "_append",
value: function _append(tooltipNode, container) {
container.appendChild(tooltipNode);
}
}, {
key: "_setEventListeners",
value: function _setEventListeners(reference, events, options) {
var _this7 = this;
var directEvents = [];
var oppositeEvents = [];
events.forEach(function (event) {
switch (event) {
case 'hover':
directEvents.push('mouseenter');
oppositeEvents.push('mouseleave');
if (_this7.options.hideOnTargetClick) oppositeEvents.push('click');
break;
case 'focus':
directEvents.push('focus');
oppositeEvents.push('blur');
if (_this7.options.hideOnTargetClick) oppositeEvents.push('click');
break;
case 'click':
directEvents.push('click');
oppositeEvents.push('click');
break;
}
});
// schedule show tooltip
directEvents.forEach(function (event) {
var func = function func(evt) {
if (_this7._isOpen === true) {
return;
}
evt.usedByTooltip = true;
_this7._scheduleShow(reference, options.delay, options, evt);
};
_this7._events.push({
event: event,
func: func
});
reference.addEventListener(event, func);
});
// schedule hide tooltip
oppositeEvents.forEach(function (event) {
var func = function func(evt) {
if (evt.usedByTooltip === true) {
return;
}
_this7._scheduleHide(reference, options.delay, options, evt);
};
_this7._events.push({
event: event,
func: func
});
reference.addEventListener(event, func);
});
}
}, {
key: "_onDocumentTouch",
value: function _onDocumentTouch(event) {
if (this._enableDocumentTouch) {
this._scheduleHide(this.reference, this.options.delay, this.options, event);
}
}
}, {
key: "_scheduleShow",
value: function _scheduleShow(reference, delay, options /*, evt */) {
var _this8 = this;
// defaults to 0
var computedDelay = delay && delay.show || delay || 0;
clearTimeout(this._scheduleTimer);
this._scheduleTimer = window.setTimeout(function () {
return _this8._show(reference, options);
}, computedDelay);
}
}, {
key: "_scheduleHide",
value: function _scheduleHide(reference, delay, options, evt) {
var _this9 = this;
// defaults to 0
var computedDelay = delay && delay.hide || delay || 0;
clearTimeout(this._scheduleTimer);
this._scheduleTimer = window.setTimeout(function () {
if (_this9._isOpen === false) {
return;
}
if (!_this9.options.rootNode.contains(_this9._tooltipNode)) {
return;
}
// if we are hiding because of a mouseleave, we must check that the new
// reference isn't the tooltip, because in this case we don't want to hide it
if (evt.type === 'mouseleave') {
var isSet = _this9._setTooltipNodeEvent(evt, reference, delay, options);
// if we set the new event, don't hide the tooltip yet
// the new event will take care to hide it if necessary
if (isSet) {
return;
}
}
_this9._hide(reference, options);
}, computedDelay);
}
}]);
return Tooltip;
}(); // Hide tooltips on touch devices
if (typeof document !== 'undefined') {
document.addEventListener('touchstart', function (event) {
for (var i = 0; i < openTooltips.length; i++) {
openTooltips[i]._onDocumentTouch(event);
}
}, supportsPassive ? {
passive: true,
capture: true
} : true);
}
/**
* Placement function, its context is the Tooltip instance.
* @memberof Tooltip
* @callback PlacementFunction
* @param {HTMLElement} tooltip - tooltip DOM node.
* @param {HTMLElement} reference - reference DOM node.
* @return {String} placement - One of the allowed placement options.
*/
/**
* Title function, its context is the Tooltip instance.
* @memberof Tooltip
* @callback TitleFunction
* @return {String} placement - The desired title.
*/
var state = {
enabled: true
};
var positions = ['top', 'top-start', 'top-end', 'right', 'right-start', 'right-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end'];
var defaultOptions = {
// Default tooltip placement relative to target element
defaultPlacement: 'top',
// Default CSS classes applied to the tooltip element
defaultClass: 'vue-tooltip-theme',
// Default CSS classes applied to the target element of the tooltip
defaultTargetClass: 'has-tooltip',
// Is the content HTML by default?
defaultHtml: true,
// Default HTML template of the tooltip element
// It must include `tooltip-arrow` & `tooltip-inner` CSS classes (can be configured, see below)
// Change if the classes conflict with other libraries (for example bootstrap)
defaultTemplate: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
// Selector used to get the arrow element in the tooltip template
defaultArrowSelector: '.tooltip-arrow, .tooltip__arrow',
// Selector used to get the inner content element in the tooltip template
defaultInnerSelector: '.tooltip-inner, .tooltip__inner',
// Delay (ms)
defaultDelay: 0,
// Default events that trigger the tooltip
defaultTrigger: 'hover focus',
// Default position offset (px)
defaultOffset: 0,
// Default container where the tooltip will be appended
defaultContainer: 'body',
defaultBoundariesElement: undefined,
defaultPopperOptions: {},
// Class added when content is loading
defaultLoadingClass: 'tooltip-loading',
// Displayed when tooltip content is loading
defaultLoadingContent: '...',
defaultRootNode: document.body,
// Hide on mouseover tooltip
autoHide: true,
// Close tooltip on click on tooltip target?
defaultHideOnTargetClick: true,
// Auto destroy tooltip DOM nodes (ms)
disposeTimeout: 5000,
// Options for popover
popover: {
defaultPlacement: 'bottom',
// Use the `popoverClass` prop for theming
defaultClass: 'vue-popover-theme',
// Base class (change if conflicts with other libraries)
defaultBaseClass: 'tooltip popover',
// Wrapper class (contains arrow and inner)
defaultWrapperClass: 'wrapper',
// Inner content class
defaultInnerClass: 'tooltip-inner popover-inner',
// Arrow class
defaultArrowClass: 'tooltip-arrow popover-arrow',
// Class added when popover is open
defaultOpenClass: 'open',
defaultDelay: 0,
defaultTrigger: 'click',
defaultOffset: 0,
defaultContainer: 'body',
defaultBoundariesElement: undefined,
defaultPopperOptions: {},
// Hides if clicked outside of popover
defaultAutoHide: true,
// Update popper on content resize
defaultHandleResize: true
}
};
function getOptions(options) {
var result = {
placement: typeof options.placement !== 'undefined' ? options.placement : directive.options.defaultPlacement,
delay: typeof options.delay !== 'undefined' ? options.delay : directive.options.defaultDelay,
html: typeof options.html !== 'undefined' ? options.html : directive.options.defaultHtml,
template: typeof options.template !== 'undefined' ? options.template : directive.options.defaultTemplate,
arrowSelector: typeof options.arrowSelector !== 'undefined' ? options.arrowSelector : directive.options.defaultArrowSelector,
innerSelector: typeof options.innerSelector !== 'undefined' ? options.innerSelector : directive.options.defaultInnerSelector,
trigger: typeof options.trigger !== 'undefined' ? options.trigger : directive.options.defaultTrigger,
offset: typeof options.offset !== 'undefined' ? options.offset : directive.options.defaultOffset,
container: typeof options.container !== 'undefined' ? options.container : directive.options.defaultContainer,
boundariesElement: typeof options.boundariesElement !== 'undefined' ? options.boundariesElement : directive.options.defaultBoundariesElement,
autoHide: typeof options.autoHide !== 'undefined' ? options.autoHide : directive.options.autoHide,
hideOnTargetClick: typeof options.hideOnTargetClick !== 'undefined' ? options.hideOnTargetClick : directive.options.defaultHideOnTargetClick,
loadingClass: typeof options.loadingClass !== 'undefined' ? options.loadingClass : directive.options.defaultLoadingClass,
loadingContent: typeof options.loadingContent !== 'undefined' ? options.loadingContent : directive.options.defaultLoadingContent,
rootNode: typeof options.rootNode !== 'undefined' ? options.rootNode : directive.options.defaultRootNode,
popperOptions: _objectSpread2({}, typeof options.popperOptions !== 'undefined' ? options.popperOptions : directive.options.defaultPopperOptions)
};
if (result.offset) {
var typeofOffset = _typeof(result.offset);
var offset = result.offset;
// One value -> switch
if (typeofOffset === 'number' || typeofOffset === 'string' && offset.indexOf(',') === -1) {
offset = "0, ".concat(offset);
}
if (!result.popperOptions.modifiers) {
result.popperOptions.modifiers = {};
}
result.popperOptions.modifiers.offset = {
offset: offset
};
}
if (result.trigger && result.trigger.indexOf('click') !== -1) {
result.hideOnTargetClick = false;
}
return result;
}
function getPlacement(value, modifiers) {
var placement = value.placement;
for (var i = 0; i < positions.length; i++) {
var pos = positions[i];
if (modifiers[pos]) {
placement = pos;
}
}
return placement;
}
function getContent(value) {
var type = _typeof(value);
if (type === 'string') {
return value;
} else if (value && type === 'object') {
return value.content;
} else {
return false;
}
}
function createTooltip(el, value) {
var modifiers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var content = getContent(value);
var classes = typeof value.classes !== 'undefined' ? value.classes : directive.options.defaultClass;
var opts = _objectSpread2({
title: content
}, getOptions(_objectSpread2(_objectSpread2({}, value), {}, {
placement: getPlacement(value, modifiers)
})));
var tooltip = el._tooltip = new Tooltip(el, opts);
tooltip.setClasses(classes);
tooltip._vueEl = el;
// Class on target
var targetClasses = typeof value.targetClasses !== 'undefined' ? value.targetClasses : directive.options.defaultTargetClass;
el._tooltipTargetClasses = targetClasses;
addClasses(el, targetClasses);
return tooltip;
}
function destroyTooltip(el) {
if (el._tooltip) {
el._tooltip.dispose();
delete el._tooltip;
delete el._tooltipOldShow;
}
if (el._tooltipTargetClasses) {
removeClasses(el, el._tooltipTargetClasses);
delete el._tooltipTargetClasses;
}
}
function bind(el, _ref) {
var value = _ref.value,
oldValue = _ref.oldValue,
modifiers = _ref.modifiers;
var content = getContent(value);
if (!content || !state.enabled) {
destroyTooltip(el);
} else {
var tooltip;
if (el._tooltip) {
tooltip = el._tooltip;
// Content
tooltip.setContent(content);
// Options
tooltip.setOptions(_objectSpread2(_objectSpread2({}, value), {}, {
placement: getPlacement(value, modifiers)
}));
} else {
tooltip = createTooltip(el, value, modifiers);
}
// Manual show
if (typeof value.show !== 'undefined' && value.show !== el._tooltipOldShow) {
el._tooltipOldShow = value.show;
value.show ? tooltip.show() : tooltip.hide();
}
}
}
var directive = {
options: defaultOptions,
bind: bind,
update: bind,
unbind: function unbind(el) {
destroyTooltip(el);
}
};
function addListeners(el) {
el.addEventListener('click', onClick);
el.addEventListener('touchstart', onTouchStart, supportsPassive ? {
passive: true
} : false);
}
function removeListeners(el) {
el.removeEventListener('click', onClick);
el.removeEventListener('touchstart', onTouchStart);
el.removeEventListener('touchend', onTouchEnd);
el.removeEventListener('touchcancel', onTouchCancel);
}
function onClick(event) {
var el = event.currentTarget;
event.closePopover = !el.$_vclosepopover_touch;
event.closeAllPopover = el.$_closePopoverModifiers && !!el.$_closePopoverModifiers.all;
}
function onTouchStart(event) {
if (event.changedTouches.length === 1) {
var el = event.currentTarget;
el.$_vclosepopover_touch = true;
var touch = event.changedTouches[0];
el.$_vclosepopover_touchPoint = touch;
el.addEventListener('touchend', onTouchEnd);
el.addEventListener('touchcancel', onTouchCancel);
}
}
function onTouchEnd(event) {
var el = event.currentTarget;
el.$_vclosepopover_touch = false;
if (event.changedTouches.length === 1) {
var touch = event.changedTouches[0];
var firstTouch = el.$_vclosepopover_touchPoint;
event.closePopover = Math.abs(touch.screenY - firstTouch.screenY) < 20 && Math.abs(touch.screenX - firstTouch.screenX) < 20;
event.closeAllPopover = el.$_closePopoverModifiers && !!el.$_closePopoverModifiers.all;
}
}
function onTouchCancel(event) {
var el = event.currentTarget;
el.$_vclosepopover_touch = false;
}
var vclosepopover = {
bind: function bind(el, _ref) {
var value = _ref.value,
modifiers = _ref.modifiers;
el.$_closePopoverModifiers = modifiers;
if (typeof value === 'undefined' || value) {
addListeners(el);
}
},
update: function update(el, _ref2) {
var value = _ref2.value,
oldValue = _ref2.oldValue,
modifiers = _ref2.modifiers;
el.$_closePopoverModifiers = modifiers;
if (value !== oldValue) {
if (typeof value === 'undefined' || value) {
addListeners(el);
} else {
removeListeners(el);
}
}
},
unbind: function unbind(el) {
removeListeners(el);
}
};
function getDefault(key) {
var value = directive.options.popover[key];
if (typeof value === 'undefined') {
return directive.options[key];
}
return value;
}
var isIOS = false;
if (typeof window !== 'undefined' && typeof navigator !== 'undefined') {
isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
}
var openPopovers = [];
var Element = function Element() {};
if (typeof window !== 'undefined') {
Element = window.Element;
}
var script = {
name: 'VPopover',
components: {
ResizeObserver: ResizeObserver
},
props: {
open: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
placement: {
type: String,
default: function _default() {
return getDefault('defaultPlacement');
}
},
delay: {
type: [String, Number, Object],
default: function _default() {
return getDefault('defaultDelay');
}
},
offset: {
type: [String, Number],
default: function _default() {
return getDefault('defaultOffset');
}
},
trigger: {
type: String,
default: function _default() {
return getDefault('defaultTrigger');
}
},
container: {
type: [String, Object, Element, Boolean],
default: function _default() {
return getDefault('defaultContainer');
}
},
boundariesElement: {
type: [String, Element],
default: function _default() {
return getDefault('defaultBoundariesElement');
}
},
popperOptions: {
type: Object,
default: function _default() {
return getDefault('defaultPopperOptions');
}
},
popoverClass: {
type: [String, Array],
default: function _default() {
return getDefault('defaultClass');
}
},
popoverBaseClass: {
type: [String, Array],
default: function _default() {
return directive.options.popover.defaultBaseClass;
}
},
popoverInnerClass: {
type: [String, Array],
default: function _default() {
return directive.options.popover.defaultInnerClass;
}
},
popoverWrapperClass: {
type: [String, Array],
default: function _default() {
return directive.options.popover.defaultWrapperClass;
}
},
popoverArrowClass: {
type: [String, Array],
default: function _default() {
return directive.options.popover.defaultArrowClass;
}
},
autoHide: {
type: Boolean,
default: function _default() {
return directive.options.popover.defaultAutoHide;
}
},
handleResize: {
type: Boolean,
default: function _default() {
return directive.options.popover.defaultHandleResize;
}
},
openGroup: {
type: String,
default: null
},
openClass: {
type: [String, Array],
default: function _default() {
return directive.options.popover.defaultOpenClass;
}
}
},
data: function data() {
return {
isOpen: false,
id: Math.random().toString(36).substr(2, 10)
};
},
computed: {
cssClass: function cssClass() {
return _defineProperty({}, this.openClass, this.isOpen);
},
popoverId: function popoverId() {
return "popover_".concat(this.id);
}
},
watch: {
open: function open(val) {
if (val) {
this.show();
} else {
this.hide();
}
},
disabled: function disabled(val, oldVal) {
if (val !== oldVal) {
if (val) {
this.hide();
} else if (this.open) {
this.show();
}
}
},
container: function container(val) {
if (this.isOpen && this.popperInstance) {
var popoverNode = this.$refs.popover;
var reference = this.$refs.trigger;
var container = this.$_findContainer(this.container, reference);
if (!container) {
console.warn('No container for popover', this);
return;
}
container.appendChild(popoverNode);
this.popperInstance.scheduleUpdate();
}
},
trigger: function trigger(val) {
this.$_removeEventListeners();
this.$_addEventListeners();
},
placement: function placement(val) {
var _this = this;
this.$_updatePopper(function () {
_this.popperInstance.options.placement = val;
});
},
offset: '$_restartPopper',
boundariesElement: '$_restartPopper',
popperOptions: {
handler: '$_restartPopper',
deep: true
}
},
created: function created() {
this.$_isDisposed = false;
this.$_mounted = false;
this.$_events = [];
this.$_preventOpen = false;
},
mounted: function mounted() {
var popoverNode = this.$refs.popover;
popoverNode.parentNode && popoverNode.parentNode.removeChild(popoverNode);
this.$_init();
if (this.open) {
this.show();
}
},
deactivated: function deactivated() {
this.hide();
},
beforeDestroy: function beforeDestroy() {
this.dispose();
},
methods: {
show: function show() {
var _this2 = this;
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
event = _ref2.event,
_ref2$skipDelay = _ref2.skipDelay,
_ref2$force = _ref2.force,
force = _ref2$force === void 0 ? false : _ref2$force;
if (force || !this.disabled) {
this.$_scheduleShow(event);
this.$emit('show');
}
this.$emit('update:open', true);
this.$_beingShowed = true;
requestAnimationFrame(function () {
_this2.$_beingShowed = false;
});
},
hide: function hide() {
var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
event = _ref3.event,
_ref3$skipDelay = _ref3.skipDelay;
this.$_scheduleHide(event);
this.$emit('hide');
this.$emit('update:open', false);
},
dispose: function dispose() {
this.$_isDisposed = true;
this.$_removeEventListeners();
this.hide({
skipDelay: true
});
if (this.popperInstance) {
this.popperInstance.destroy();
// destroy tooltipNode if removeOnDestroy is not set, as popperInstance.destroy() already removes the element
if (!this.popperInstance.options.removeOnDestroy) {
var popoverNode = this.$refs.popover;
popoverNode.parentNode && popoverNode.parentNode.removeChild(popoverNode);
}
}
this.$_mounted = false;
this.popperInstance = null;
this.isOpen = false;
this.$emit('dispose');
},
$_init: function $_init() {
if (this.trigger.indexOf('manual') === -1) {
this.$_addEventListeners();
}
},
$_show: function $_show() {
var _this3 = this;
var reference = this.$refs.trigger;
var popoverNode = this.$refs.popover;
clearTimeout(this.$_disposeTimer);
// Already open
if (this.isOpen) {
return;
}
// Popper is already initialized
if (this.popperInstance) {
this.isOpen = true;
this.popperInstance.enableEventListeners();
this.popperInstance.scheduleUpdate();
}
if (!this.$_mounted) {
var container = this.$_findContainer(this.container, reference);
if (!container) {
console.warn('No container for popover', this);
return;
}
container.appendChild(popoverNode);
this.$_mounted = true;
}
if (!this.popperInstance) {
var popperOptions = _objectSpread2(_objectSpread2({}, this.popperOptions), {}, {
placement: this.placement
});
popperOptions.modifiers = _objectSpread2(_objectSpread2({}, popperOptions.modifiers), {}, {
arrow: _objectSpread2(_objectSpread2({}, popperOptions.modifiers && popperOptions.modifiers.arrow), {}, {
element: this.$refs.arrow
})
});
if (this.offset) {
var offset = this.$_getOffset();
popperOptions.modifiers.offset = _objectSpread2(_objectSpread2({}, popperOptions.modifiers && popperOptions.modifiers.offset), {}, {
offset: offset
});
}
if (this.boundariesElement) {
popperOptions.modifiers.preventOverflow = _objectSpread2(_objectSpread2({}, popperOptions.modifiers && popperOptions.modifiers.preventOverflow), {}, {
boundariesElement: this.boundariesElement
});
}
this.popperInstance = new Popper(reference, popoverNode, popperOptions);
// Fix position
requestAnimationFrame(function () {
if (_this3.hidden) {
_this3.hidden = false;
_this3.$_hide();
return;
}
if (!_this3.$_isDisposed && _this3.popperInstance) {
_this3.popperInstance.scheduleUpdate();
// Show the tooltip
requestAnimationFrame(function () {
if (_this3.hidden) {
_this3.hidden = false;
_this3.$_hide();
return;
}
if (!_this3.$_isDisposed) {
_this3.isOpen = true;
} else {
_this3.dispose();
}
});
} else {
_this3.dispose();
}
});
}
var openGroup = this.openGroup;
if (openGroup) {
var popover;
for (var i = 0; i < openPopovers.length; i++) {
popover = openPopovers[i];
if (popover.openGroup !== openGroup) {
popover.hide();
popover.$emit('close-group');
}
}
}
openPopovers.push(this);
this.$emit('apply-show');
},
$_hide: function $_hide() {
var _this4 = this;
// Already hidden
if (!this.isOpen) {
return;
}
var index = openPopovers.indexOf(this);
if (index !== -1) {
openPopovers.splice(index, 1);
}
this.isOpen = false;
if (this.popperInstance) {
this.popperInstance.disableEventListeners();
}
clearTimeout(this.$_disposeTimer);
var disposeTime = directive.options.popover.disposeTimeout || directive.options.disposeTimeout;
if (disposeTime !== null) {
this.$_disposeTimer = setTimeout(function () {
var popoverNode = _this4.$refs.popover;
if (popoverNode) {
// Don't remove popper instance, just the HTML element
popoverNode.parentNode && popoverNode.parentNode.removeChild(popoverNode);
_this4.$_mounted = false;
}
}, disposeTime);
}
this.$emit('apply-hide');
},
$_findContainer: function $_findContainer(container, reference) {
// if container is a query, get the relative element
if (typeof container === 'string') {
container = directive.options.rootNode.querySelector(container);
} else if (container === false) {
// if container is `false`, set it to reference parent
container = reference.parentNode;
}
return container;
},
$_getOffset: function $_getOffset() {
var typeofOffset = _typeof(this.offset);
var offset = this.offset;
// One value -> switch
if (typeofOffset === 'number' || typeofOffset === 'string' && offset.indexOf(',') === -1) {
offset = "0, ".concat(offset);
}
return offset;
},
$_addEventListeners: function $_addEventListeners() {
var _this5 = this;
var reference = this.$refs.trigger;
var directEvents = [];
var oppositeEvents = [];
var events = typeof this.trigger === 'string' ? this.trigger.split(' ').filter(function (trigger) {
return ['click', 'hover', 'focus'].indexOf(trigger) !== -1;
}) : [];
events.forEach(function (event) {
switch (event) {
case 'hover':
directEvents.push('mouseenter');
oppositeEvents.push('mouseleave');
break;
case 'focus':
directEvents.push('focus');
oppositeEvents.push('blur');
break;
case 'click':
directEvents.push('click');
oppositeEvents.push('click');
break;
}
});
// schedule show tooltip
directEvents.forEach(function (event) {
var func = function func(event) {
if (_this5.isOpen) {
return;
}
event.usedByTooltip = true;
!_this5.$_preventOpen && _this5.show({
event: event
});
_this5.hidden = false;
};
_this5.$_events.push({
event: event,
func: func
});
reference.addEventListener(event, func);
});
// schedule hide tooltip
oppositeEvents.forEach(function (event) {
var func = function func(event) {
if (event.usedByTooltip) {
re