bootstrap-vue
Version:
BootstrapVue, with over 40 plugins and more than 80 custom components, custom directives, and over 300 icons, provides one of the most comprehensive implementations of Bootstrap v4 components and grid system for Vue.js. With extensive and automated WAI-AR
351 lines (324 loc) • 10.4 kB
JavaScript
import Vue from '../../utils/vue';
import getScopId from '../../utils/get-scope-id';
import { isArray, arrayIncludes } from '../../utils/array';
import { getComponentConfig } from '../../utils/config';
import { isString, isUndefinedOrNull } from '../../utils/inspect';
import { HTMLElement, SVGElement } from '../../utils/safe-types';
import { BVTooltip } from './helpers/bv-tooltip';
var NAME = 'BTooltip'; // @vue/component
export var BTooltip =
/*#__PURE__*/
Vue.extend({
name: NAME,
props: {
title: {
type: String // default: undefined
},
// Added in by BPopover
// content: {
// type: String,
// default: undefined
// },
target: {
// String ID of element, or element/component reference
// Or function that returns one of the above
type: [String, HTMLElement, SVGElement, Function, Object],
// default: undefined,
required: true
},
triggers: {
type: [String, Array],
default: 'hover focus'
},
placement: {
type: String,
default: 'top'
},
fallbackPlacement: {
type: [String, Array],
default: 'flip',
validator: function validator(value) {
return isArray(value) && value.every(function (v) {
return isString(v);
}) || arrayIncludes(['flip', 'clockwise', 'counterclockwise'], value);
}
},
variant: {
type: String,
default: function _default() {
return getComponentConfig(NAME, 'variant');
}
},
customClass: {
type: String,
default: function _default() {
return getComponentConfig(NAME, 'customClass');
}
},
delay: {
type: [Number, Object, String],
default: function _default() {
return getComponentConfig(NAME, 'delay');
}
},
boundary: {
// String: scrollParent, window, or viewport
// Element: element reference
// Object: Vue component
type: [String, HTMLElement, Object],
default: function _default() {
return getComponentConfig(NAME, 'boundary');
}
},
boundaryPadding: {
type: [Number, String],
default: function _default() {
return getComponentConfig(NAME, 'boundaryPadding');
}
},
offset: {
type: [Number, String],
default: 0
},
noFade: {
type: Boolean,
default: false
},
container: {
// String: HTML ID of container, if null body is used (default)
// HTMLElement: element reference reference
// Object: Vue Component
type: [String, HTMLElement, Object] // default: undefined
},
show: {
type: Boolean,
default: false
},
noninteractive: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
id: {
// ID to use for tooltip element
// If not provided on will automatically be generated
type: String,
default: null
}
},
data: function data() {
return {
localShow: this.show,
localTitle: '',
localContent: ''
};
},
computed: {
templateData: function templateData() {
// Data that will be passed to the template and popper
return {
// We use massaged versions of the title and content props/slots
title: this.localTitle,
content: this.localContent,
// Pass these props as is
target: this.target,
triggers: this.triggers,
placement: this.placement,
fallbackPlacement: this.fallbackPlacement,
variant: this.variant,
customClass: this.customClass,
container: this.container,
boundary: this.boundary,
boundaryPadding: this.boundaryPadding,
delay: this.delay,
offset: this.offset,
noFade: this.noFade,
interactive: !this.noninteractive,
disabled: this.disabled,
id: this.id
};
},
templateTitleContent: function templateTitleContent() {
// Used to watch for changes to the title and content props
return {
title: this.title,
content: this.content
};
}
},
watch: {
show: function show(_show, oldVal) {
if (_show !== oldVal && _show !== this.localShow && this.$_bv_toolpop) {
if (_show) {
this.$_bv_toolpop.show();
} else {
// We use `forceHide()` to override any active triggers
this.$_bv_toolpop.forceHide();
}
}
},
disabled: function disabled(newVal, oldVal) {
if (newVal) {
this.doDisable();
} else {
this.doEnable();
}
},
localShow: function localShow(show, oldVal) {
// TODO: May need to be done in a `$nextTick()`
this.$emit('update:show', show);
},
templateData: function templateData(newVal, oldVal) {
var _this = this;
this.$nextTick(function () {
if (_this.$_bv_toolpop) {
_this.$_bv_toolpop.updateData(_this.templateData);
}
});
},
// Watchers for title/content props (prop changes do not trigger the `updated()` hook)
templateTitleContent: function templateTitleContent(newVal, oldVal) {
this.$nextTick(this.updateContent);
}
},
created: function created() {
// Non reactive properties
this.$_bv_toolpop = null;
},
updated: function updated() {
// Update the `propData` object
// Done in a `$nextTick()` to ensure slot(s) have updated
this.$nextTick(this.updateContent);
},
beforeDestroy: function beforeDestroy() {
// Shutdown our local event listeners
this.$off('open', this.doOpen);
this.$off('close', this.doClose);
this.$off('disable', this.doDisable);
this.$off('enable', this.doEnable); // Destroy the tip instance
this.$_bv_toolpop && this.$_bv_toolpop.$destroy();
this.$_bv_toolpop = null;
},
mounted: function mounted() {
var _this2 = this;
// Instantiate a new BVTooltip instance
// Done in a `$nextTick()` to ensure DOM has completed rendering
// so that target can be found
this.$nextTick(function () {
// Load the on demand child instance
var Component = _this2.getComponent(); // Ensure we have initial content
_this2.updateContent(); // Pass down the scoped style attribute if available
var scopeId = getScopId(_this2) || getScopId(_this2.$parent); // Create the instance
var $toolpop = _this2.$_bv_toolpop = new Component({
parent: _this2,
// Pass down the scoped style ID
_scopeId: scopeId || undefined
}); // Set the initial data
$toolpop.updateData(_this2.templateData); // Set listeners
$toolpop.$on('show', _this2.onShow);
$toolpop.$on('shown', _this2.onShown);
$toolpop.$on('hide', _this2.onHide);
$toolpop.$on('hidden', _this2.onHidden);
$toolpop.$on('disabled', _this2.onDisabled);
$toolpop.$on('enabled', _this2.onEnabled); // Initially disabled?
if (_this2.disabled) {
// Initially disabled
_this2.doDisable();
} // Listen to open signals from others
_this2.$on('open', _this2.doOpen); // Listen to close signals from others
_this2.$on('close', _this2.doClose); // Listen to disable signals from others
_this2.$on('disable', _this2.doDisable); // Listen to enable signals from others
_this2.$on('enable', _this2.doEnable); // Initially show tooltip?
if (_this2.localShow) {
_this2.$_bv_toolpop && _this2.$_bv_toolpop.show();
}
});
},
methods: {
getComponent: function getComponent() {
// Overridden by BPopover
return BVTooltip;
},
updateContent: function updateContent() {
// Overridden by BPopover
// Tooltip: Default slot is `title`
// Popover: Default slot is `content`, `title` slot is title
// We pass a scoped slot function reference by default (Vue v2.6x)
// And pass the title prop as a fallback
this.setTitle(this.$scopedSlots.default || this.title);
},
// Helper methods for `updateContent()`
setTitle: function setTitle(val) {
val = isUndefinedOrNull(val) ? '' : val; // We only update the value if it has changed
if (this.localTitle !== val) {
this.localTitle = val;
}
},
setContent: function setContent(val) {
val = isUndefinedOrNull(val) ? '' : val; // We only update the value if it has changed
if (this.localContent !== val) {
this.localContent = val;
}
},
// --- Template event handlers ---
onShow: function onShow(bvEvt) {
// Placeholder
this.$emit('show', bvEvt);
if (bvEvt) {
this.localShow = !bvEvt.defaultPrevented;
}
},
onShown: function onShown(bvEvt) {
// Tip is now showing
this.localShow = true;
this.$emit('shown', bvEvt);
},
onHide: function onHide(bvEvt) {
this.$emit('hide', bvEvt);
},
onHidden: function onHidden(bvEvt) {
// Tip is no longer showing
this.$emit('hidden', bvEvt);
this.localShow = false;
},
onDisabled: function onDisabled(bvEvt) {
// Prevent possible endless loop if user mistakenly
// fires `disabled` instead of `disable`
if (bvEvt && bvEvt.type === 'disabled') {
this.$emit('update:disabled', true);
this.$emit('disabled', bvEvt);
}
},
onEnabled: function onEnabled(bvEvt) {
// Prevent possible endless loop if user mistakenly
// fires `enabled` instead of `enable`
if (bvEvt && bvEvt.type === 'enabled') {
this.$emit('update:disabled', false);
this.$emit('enabled', bvEvt);
}
},
// --- Local event listeners ---
doOpen: function doOpen() {
!this.localShow && this.$_bv_toolpop && this.$_bv_toolpop.show();
},
doClose: function doClose() {
this.localShow && this.$_bv_toolpop && this.$_bv_toolpop.hide();
},
doDisable: function doDisable(evt) {
this.$_bv_toolpop && this.$_bv_toolpop.disable();
},
doEnable: function doEnable() {
this.$_bv_toolpop && this.$_bv_toolpop.enable();
}
},
render: function render(h) {
// Always renders a comment node
// TODO:
// Future: Possibly render a target slot (single root element)
// which we can apply the listeners to (pass `this.$el` to BVTooltip)
return h();
}
});