UNPKG

bootstrap-vue

Version:

With more than 85 components, over 45 available plugins, several directives, and 1000+ icons, BootstrapVue provides one of the most comprehensive implementations of the Bootstrap v4 component and grid system available for Vue.js v2.6, complete with extens

351 lines (323 loc) 10.4 kB
import Vue from '../../vue'; import { NAME_TOOLTIP } from '../../constants/components'; import getScopId from '../../utils/get-scope-id'; import { arrayIncludes } from '../../utils/array'; import { getComponentConfig } from '../../utils/config'; import { isArray, isString, isUndefinedOrNull } from '../../utils/inspect'; import { HTMLElement, SVGElement } from '../../utils/safe-types'; import { BVTooltip } from './helpers/bv-tooltip'; // @vue/component export var BTooltip = /*#__PURE__*/Vue.extend({ name: NAME_TOOLTIP, inheritAttrs: false, 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], 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_TOOLTIP, 'variant'); } }, customClass: { type: String, default: function _default() { return getComponentConfig(NAME_TOOLTIP, 'customClass'); } }, delay: { type: [Number, Object, String], default: function _default() { return getComponentConfig(NAME_TOOLTIP, 'delay'); } }, boundary: { // String: scrollParent, window, or viewport // Element: element reference // Object: Vue component type: [String, HTMLElement, Object], default: function _default() { return getComponentConfig(NAME_TOOLTIP, 'boundary'); } }, boundaryPadding: { type: [Number, String], default: function _default() { return getComponentConfig(NAME_TOOLTIP, '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.$_toolpop) { if (_show) { this.$_toolpop.show(); } else { // We use `forceHide()` to override any active triggers this.$_toolpop.forceHide(); } } }, disabled: function disabled(newVal) { if (newVal) { this.doDisable(); } else { this.doEnable(); } }, localShow: function localShow(newVal) { // TODO: May need to be done in a `$nextTick()` this.$emit('update:show', newVal); }, templateData: function templateData() { var _this = this; this.$nextTick(function () { if (_this.$_toolpop) { _this.$_toolpop.updateData(_this.templateData); } }); }, // Watchers for title/content props (prop changes do not trigger the `updated()` hook) templateTitleContent: function templateTitleContent() { this.$nextTick(this.updateContent); } }, created: function created() { // Create private non-reactive props this.$_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 if (this.$_toolpop) { this.$_toolpop.$destroy(); this.$_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.$_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) { $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.$_toolpop && this.$_toolpop.show(); }, doClose: function doClose() { this.localShow && this.$_toolpop && this.$_toolpop.hide(); }, doDisable: function doDisable() { this.$_toolpop && this.$_toolpop.disable(); }, doEnable: function doEnable() { this.$_toolpop && this.$_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(); } });