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

415 lines (386 loc) 15 kB
var _watch; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(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; } import { Portal, Wormhole } from 'portal-vue'; import { COMPONENT_UID_KEY, extend } from '../../vue'; import { NAME_TOAST, NAME_TOASTER } from '../../constants/components'; import { EVENT_NAME_CHANGE, EVENT_NAME_DESTROYED, EVENT_NAME_HIDDEN, EVENT_NAME_HIDE, EVENT_NAME_SHOW, EVENT_NAME_SHOWN, EVENT_OPTIONS_NO_CAPTURE } from '../../constants/events'; import { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER_STRING, PROP_TYPE_STRING } from '../../constants/props'; import { SLOT_NAME_DEFAULT, SLOT_NAME_TOAST_TITLE } from '../../constants/slots'; import { BvEvent } from '../../utils/bv-event.class'; import { requestAF } from '../../utils/dom'; import { getRootActionEventName, getRootEventName, eventOnOff } from '../../utils/events'; import { mathMax } from '../../utils/math'; import { makeModelMixin } from '../../utils/model'; import { toInteger } from '../../utils/number'; import { pick, sortKeys } from '../../utils/object'; import { makeProp, makePropsConfigurable, pluckProps } from '../../utils/props'; import { isLink } from '../../utils/router'; import { createNewChildComponent } from '../../utils/create-new-child-component'; import { attrsMixin } from '../../mixins/attrs'; import { idMixin, props as idProps } from '../../mixins/id'; import { listenOnRootMixin } from '../../mixins/listen-on-root'; import { normalizeSlotMixin } from '../../mixins/normalize-slot'; import { scopedStyleMixin } from '../../mixins/scoped-style'; import { BButtonClose } from '../button/button-close'; import { BLink, props as BLinkProps } from '../link/link'; import { BVTransition } from '../transition/bv-transition'; import { BToaster } from './toaster'; // --- Constants --- var _makeModelMixin = makeModelMixin('visible', { type: PROP_TYPE_BOOLEAN, defaultValue: false, event: EVENT_NAME_CHANGE }), modelMixin = _makeModelMixin.mixin, modelProps = _makeModelMixin.props, MODEL_PROP_NAME = _makeModelMixin.prop, MODEL_EVENT_NAME = _makeModelMixin.event; var MIN_DURATION = 1000; // --- Props --- var linkProps = pick(BLinkProps, ['href', 'to']); export var props = makePropsConfigurable(sortKeys(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, idProps), modelProps), linkProps), {}, { appendToast: makeProp(PROP_TYPE_BOOLEAN, false), autoHideDelay: makeProp(PROP_TYPE_NUMBER_STRING, 5000), bodyClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), headerClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), headerTag: makeProp(PROP_TYPE_STRING, 'header'), // Switches role to 'status' and aria-live to 'polite' isStatus: makeProp(PROP_TYPE_BOOLEAN, false), noAutoHide: makeProp(PROP_TYPE_BOOLEAN, false), noCloseButton: makeProp(PROP_TYPE_BOOLEAN, false), noFade: makeProp(PROP_TYPE_BOOLEAN, false), noHoverPause: makeProp(PROP_TYPE_BOOLEAN, false), solid: makeProp(PROP_TYPE_BOOLEAN, false), // Render the toast in place, rather than in a portal-target static: makeProp(PROP_TYPE_BOOLEAN, false), title: makeProp(PROP_TYPE_STRING), toastClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING), toaster: makeProp(PROP_TYPE_STRING, 'b-toaster-top-right'), variant: makeProp(PROP_TYPE_STRING) })), NAME_TOAST); // --- Main component --- // @vue/component export var BToast = /*#__PURE__*/extend({ name: NAME_TOAST, mixins: [attrsMixin, idMixin, modelMixin, listenOnRootMixin, normalizeSlotMixin, scopedStyleMixin], inheritAttrs: false, props: props, data: function data() { return { isMounted: false, doRender: false, localShow: false, isTransitioning: false, isHiding: false, order: 0, dismissStarted: 0, resumeDismiss: 0 }; }, computed: { toastClasses: function toastClasses() { var appendToast = this.appendToast, variant = this.variant; return _defineProperty({ 'b-toast-solid': this.solid, 'b-toast-append': appendToast, 'b-toast-prepend': !appendToast }, "b-toast-".concat(variant), variant); }, slotScope: function slotScope() { var hide = this.hide; return { hide: hide }; }, computedDuration: function computedDuration() { // Minimum supported duration is 1 second return mathMax(toInteger(this.autoHideDelay, 0), MIN_DURATION); }, computedToaster: function computedToaster() { return String(this.toaster); }, transitionHandlers: function transitionHandlers() { return { beforeEnter: this.onBeforeEnter, afterEnter: this.onAfterEnter, beforeLeave: this.onBeforeLeave, afterLeave: this.onAfterLeave }; }, computedAttrs: function computedAttrs() { return _objectSpread(_objectSpread({}, this.bvAttrs), {}, { id: this.safeId(), tabindex: '0' }); } }, watch: (_watch = {}, _defineProperty(_watch, MODEL_PROP_NAME, function (newValue) { this[newValue ? 'show' : 'hide'](); }), _defineProperty(_watch, "localShow", function localShow(newValue) { if (newValue !== this[MODEL_PROP_NAME]) { this.$emit(MODEL_EVENT_NAME, newValue); } }), _defineProperty(_watch, "toaster", function toaster() { // If toaster target changed, make sure toaster exists this.$nextTick(this.ensureToaster); }), _defineProperty(_watch, "static", function _static(newValue) { // If static changes to true, and the toast is showing, // ensure the toaster target exists if (newValue && this.localShow) { this.ensureToaster(); } }), _watch), created: function created() { // Create private non-reactive props this.$_dismissTimer = null; }, mounted: function mounted() { var _this = this; this.isMounted = true; this.$nextTick(function () { if (_this[MODEL_PROP_NAME]) { requestAF(function () { _this.show(); }); } }); // Listen for global $root show events this.listenOnRoot(getRootActionEventName(NAME_TOAST, EVENT_NAME_SHOW), function (id) { if (id === _this.safeId()) { _this.show(); } }); // Listen for global $root hide events this.listenOnRoot(getRootActionEventName(NAME_TOAST, EVENT_NAME_HIDE), function (id) { if (!id || id === _this.safeId()) { _this.hide(); } }); // Make sure we hide when toaster is destroyed /* istanbul ignore next: difficult to test */ this.listenOnRoot(getRootEventName(NAME_TOASTER, EVENT_NAME_DESTROYED), function (toaster) { /* istanbul ignore next */ if (toaster === _this.computedToaster) { _this.hide(); } }); }, beforeDestroy: function beforeDestroy() { this.clearDismissTimer(); }, methods: { show: function show() { var _this2 = this; if (!this.localShow) { this.ensureToaster(); var showEvent = this.buildEvent(EVENT_NAME_SHOW); this.emitEvent(showEvent); this.dismissStarted = this.resumeDismiss = 0; this.order = Date.now() * (this.appendToast ? 1 : -1); this.isHiding = false; this.doRender = true; this.$nextTick(function () { // We show the toast after we have rendered the portal and b-toast wrapper // so that screen readers will properly announce the toast requestAF(function () { _this2.localShow = true; }); }); } }, hide: function hide() { var _this3 = this; if (this.localShow) { var hideEvent = this.buildEvent(EVENT_NAME_HIDE); this.emitEvent(hideEvent); this.setHoverHandler(false); this.dismissStarted = this.resumeDismiss = 0; this.clearDismissTimer(); this.isHiding = true; requestAF(function () { _this3.localShow = false; }); } }, buildEvent: function buildEvent(type) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return new BvEvent(type, _objectSpread(_objectSpread({ cancelable: false, target: this.$el || null, relatedTarget: null }, options), {}, { vueTarget: this, componentId: this.safeId() })); }, emitEvent: function emitEvent(bvEvent) { var type = bvEvent.type; this.emitOnRoot(getRootEventName(NAME_TOAST, type), bvEvent); this.$emit(type, bvEvent); }, ensureToaster: function ensureToaster() { if (this.static) { return; } var computedToaster = this.computedToaster; if (!Wormhole.hasTarget(computedToaster)) { var div = document.createElement('div'); document.body.appendChild(div); var toaster = createNewChildComponent(this.bvEventRoot, BToaster, { propsData: { name: computedToaster } }); toaster.$mount(div); } }, startDismissTimer: function startDismissTimer() { this.clearDismissTimer(); if (!this.noAutoHide) { this.$_dismissTimer = setTimeout(this.hide, this.resumeDismiss || this.computedDuration); this.dismissStarted = Date.now(); this.resumeDismiss = 0; } }, clearDismissTimer: function clearDismissTimer() { clearTimeout(this.$_dismissTimer); this.$_dismissTimer = null; }, setHoverHandler: function setHoverHandler(on) { var el = this.$refs['b-toast']; eventOnOff(on, el, 'mouseenter', this.onPause, EVENT_OPTIONS_NO_CAPTURE); eventOnOff(on, el, 'mouseleave', this.onUnPause, EVENT_OPTIONS_NO_CAPTURE); }, onPause: function onPause() { // Determine time remaining, and then pause timer if (this.noAutoHide || this.noHoverPause || !this.$_dismissTimer || this.resumeDismiss) { return; } var passed = Date.now() - this.dismissStarted; if (passed > 0) { this.clearDismissTimer(); this.resumeDismiss = mathMax(this.computedDuration - passed, MIN_DURATION); } }, onUnPause: function onUnPause() { // Restart timer with max of time remaining or 1 second if (this.noAutoHide || this.noHoverPause || !this.resumeDismiss) { this.resumeDismiss = this.dismissStarted = 0; return; } this.startDismissTimer(); }, onLinkClick: function onLinkClick() { var _this4 = this; // We delay the close to allow time for the // browser to process the link click this.$nextTick(function () { requestAF(function () { _this4.hide(); }); }); }, onBeforeEnter: function onBeforeEnter() { this.isTransitioning = true; }, onAfterEnter: function onAfterEnter() { this.isTransitioning = false; var hiddenEvent = this.buildEvent(EVENT_NAME_SHOWN); this.emitEvent(hiddenEvent); this.startDismissTimer(); this.setHoverHandler(true); }, onBeforeLeave: function onBeforeLeave() { this.isTransitioning = true; }, onAfterLeave: function onAfterLeave() { this.isTransitioning = false; this.order = 0; this.resumeDismiss = this.dismissStarted = 0; var hiddenEvent = this.buildEvent(EVENT_NAME_HIDDEN); this.emitEvent(hiddenEvent); this.doRender = false; }, // Render helper for generating the toast makeToast: function makeToast(h) { var _this5 = this; var title = this.title, slotScope = this.slotScope; var link = isLink(this); var $headerContent = []; var $title = this.normalizeSlot(SLOT_NAME_TOAST_TITLE, slotScope); if ($title) { $headerContent.push($title); } else if (title) { $headerContent.push(h('strong', { staticClass: 'mr-2' }, title)); } if (!this.noCloseButton) { $headerContent.push(h(BButtonClose, { staticClass: 'ml-auto mb-1', on: { click: function click() { _this5.hide(); } } })); } var $header = h(); if ($headerContent.length > 0) { $header = h(this.headerTag, { staticClass: 'toast-header', class: this.headerClass }, $headerContent); } var $body = h(link ? BLink : 'div', { staticClass: 'toast-body', class: this.bodyClass, props: link ? pluckProps(linkProps, this) : {}, on: link ? { click: this.onLinkClick } : {} }, this.normalizeSlot(SLOT_NAME_DEFAULT, slotScope)); return h('div', { staticClass: 'toast', class: this.toastClass, attrs: this.computedAttrs, key: "toast-".concat(this[COMPONENT_UID_KEY]), ref: 'toast' }, [$header, $body]); } }, render: function render(h) { if (!this.doRender || !this.isMounted) { return h(); } var order = this.order, isStatic = this.static, isHiding = this.isHiding, isStatus = this.isStatus; var name = "b-toast-".concat(this[COMPONENT_UID_KEY]); var $toast = h('div', { staticClass: 'b-toast', class: this.toastClasses, attrs: _objectSpread(_objectSpread({}, isStatic ? {} : this.scopedStyleAttrs), {}, { id: this.safeId('_toast_outer'), role: isHiding ? null : isStatus ? 'status' : 'alert', 'aria-live': isHiding ? null : isStatus ? 'polite' : 'assertive', 'aria-atomic': isHiding ? null : 'true' }), key: name, ref: 'b-toast' }, [h(BVTransition, { props: { noFade: this.noFade }, on: this.transitionHandlers }, [this.localShow ? this.makeToast(h) : h()])]); return h(Portal, { props: { name: name, to: this.computedToaster, order: order, slim: true, disabled: isStatic } }, [$toast]); } });