UNPKG

vue-on-toast

Version:

A powerful, compact, Server Side Rendering supported Toast Notification component for Vue 2

437 lines (359 loc) 12.2 kB
/** * vue-on-toast v1.0.1 * (c) 2018 Stabzs * @license MIT */ var ToastServiceBus; function installBus(_Vue) { if (!_Vue) { throw new Error('Vue is not installed') } ToastServiceBus = new _Vue(); ToastServiceBus.subscribers = []; } var v = 'vot'; // Toast Types var types = { success: 'success', error: 'error', info: 'info', wait: 'wait', warning: 'warning' }; // Events var ADD_TOAST = 'addToast'; var REMOVE_TOAST = 'removeToast'; // Toast Type style class names var SUCCESS_TYPE_CLASS = v + '-' + types.success; var ERROR_TYPE_CLASS = v + '-' + types.error; var INFO_TYPE_CLASS = v + '-' + types.info; var WAIT_TYPE_CLASS = v + '-' + types.wait; var WARNING_TYPE_CLASS = v + '-' + types.warning; // Toast icon style class names var SUCCESS_ICON_CLASS = v + '-icon-' + types.success; var ERROR_ICON_CLASS = v + '-icon-' + types.error; var INFO_ICON_CLASS = v + '-icon-' + types.info; var WAIT_ICON_CLASS = v + '-icon-' + types.wait; var WARNING_ICON_CLASS = v + '-icon-' + types.warning; // Toast style class names var TITLE_CLASS = v + '-title'; var BODY_CLASS = v + '-body'; // Container position style class names var TOP_RIGHT_POSITION_CLASS = v + '-top-right'; var CLOSE_HTML = '<button class="toast-close-button" type="button">&times;</button>'; // Animation types var animations = { FADE: 'fade', EASE_OUT_LEFT: 'ease-out-left', EASE_OUT_RIGHT: 'ease-out-right' }; var Timer = { configureTimer: function configureTimer(toast) { var timeout = (typeof toast.timeout === 'number') ? toast.timeout : toast.toastConfig.timeout; if (typeof timeout === 'object') { timeout = timeout[toast.type]; } if (timeout > 0) { toast.timeoutId = setTimeout(function () { ToastServiceBus.$emit(REMOVE_TOAST, toast.toastId, toast.toastContainerId); }, timeout); } } } function ToastConfig(toastConfig) { var toastConfigDefaults = { animation: animations.FADE, bodyClass: BODY_CLASS, closeHtml: CLOSE_HTML, defaultTypeClass: INFO_TYPE_CLASS, typeClasses: { error: ERROR_TYPE_CLASS, info: INFO_TYPE_CLASS, wait: WAIT_TYPE_CLASS, success: SUCCESS_TYPE_CLASS, warning: WARNING_TYPE_CLASS }, iconClasses: { error: ERROR_ICON_CLASS, info: INFO_ICON_CLASS, wait: WAIT_ICON_CLASS, success: SUCCESS_ICON_CLASS, warning: WARNING_ICON_CLASS }, mouseoverTimerStop: false, newestOnTop: true, positionClass: TOP_RIGHT_POSITION_CLASS, preventDuplicates: false, tapToDismiss: true, timeout: 5000, titleClass: TITLE_CLASS, toastContainerId: null, showCloseButton: false }; toastConfig = Object.assign(toastConfigDefaults, toastConfig); toastConfig.typeClasses = Object.assign( toastConfigDefaults.typeClasses, toastConfig.typeClasses); toastConfig.iconClasses = Object.assign( toastConfigDefaults.iconClasses, toastConfig.iconClasses); return toastConfig } var BodyOutputType = { Component: 'Component', TrustedHtml: 'TrustedHtml' } var Toast = {render: function(){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{class:['toast', _vm.classes.typeClass],on:{"click":function($event){_vm.onClick(_vm.toast, $event);},"mouseover":function($event){_vm.stopTimer(_vm.toast);},"mouseout":function($event){_vm.restartTimer(_vm.toast);}}},[_c('i',{staticClass:"toaster-icon",class:_vm.classes.iconClass}),_vm._v(" "),_c('div',{staticClass:"toast-content"},[_c('div',{class:_vm.classes.titleClass},[_vm._v(_vm._s(_vm.toast.title))]),_vm._v(" "),_c('div',{class:_vm.classes.bodyClass},[(_vm.toast.bodyOutputType == _vm.bodyOutputType.Component)?_c(_vm.toast.body,{tag:"component"}):(_vm.toast.bodyOutputType == _vm.bodyOutputType.TrustedHtml)?_c('div',{domProps:{"innerHTML":_vm._s(_vm.toast.body)}}):(typeof _vm.toast.body === 'string')?_c('div',[_vm._v(_vm._s(_vm.toast.body))]):_vm._e()],1)]),_vm._v(" "),(_vm.toast.showCloseButton)?_c('div',{staticClass:"toast-close-button",domProps:{"innerHTML":_vm._s(_vm.toast.toastConfig.closeHtml)},on:{"click":function($event){_vm.onClick(_vm.toast, $event, true);}}}):_vm._e()])},staticRenderFns: [], name: 'toast', props: { toast: Object }, computed: { classes: function classes() { return { typeClass: this.toast.toastConfig.typeClasses[this.toast.type], iconClass: this.toast.toastConfig.iconClasses[this.toast.type], titleClass: this.toast.toastConfig.titleClass, bodyClass: this.toast.toastConfig.bodyClass } }, bodyOutputType: function bodyOutputType() { return BodyOutputType } }, methods: { onClick: function(toast, event, isCloseButton) { event.stopPropagation(); if (toast.toastConfig.tapToDismiss || (toast.showCloseButton && isCloseButton)) { var removeToast = true; if (toast.clickHandler) { if (typeof toast.clickHandler === 'function') { removeToast = toast.clickHandler(toast, isCloseButton); } else { console.log('The toast click handler is not a callable function.'); return false } } if (removeToast) { ToastServiceBus.$emit(REMOVE_TOAST, toast.toastId, toast.toastContainerId); } } }, stopTimer: function (toast) { if (toast.toastConfig.mouseoverTimerStop) { if (toast.timeoutId) { window.clearTimeout(toast.timeoutId); toast.timeoutId = null; } } }, restartTimer: function (toast) { if (toast.toastConfig.mouseoverTimerStop) { if (!toast.timeoutId) { Timer.configureTimer(toast); } } else if (toast.timeoutId === null) { ToastServiceBus.$emit(REMOVE_TOAST, toast.toastId, toast.toastContainerId ); } } } } var ToastContainer = { name: 'toast-container', data: function () { return ({ toasts: [] }); }, components: { 'toast': Toast }, props: ['toastConfig'], methods: { addToast: function addToast(toast, toastConfig) { toast.toastConfig = toastConfig; if (toast.toastContainerId && toastConfig.toastContainerId && toast.toastContainerId !== toastConfig.toastContainerId) { return } if (!toast.type) { toast.type = toastConfig.defaultTypeClass; } if (toastConfig.preventDuplicates && this.toasts.length > 0) { if (toast.toastId && this.toasts.some(function (t) { return t.toastId === toast.toastId; })) { return } else if (this.toasts.some(function (t) { return t.body === toast.body; })) { return } } this.setCloseOptions(toast, toastConfig); toast.bodyOutputType = toast.bodyOutputType || toastConfig.bodyOutputType; Timer.configureTimer(toast); if (toastConfig.newestOnTop) { this.toasts.unshift(toast); if (this.isLimitExceeded(toastConfig)) { this.toasts.pop(); } } else { this.toasts.push(toast); if (this.isLimitExceeded(toastConfig)) { this.toasts.shift(); } } if (toast.onShowCallback) { toast.onShowCallback(toast); } }, setCloseOptions: function setCloseOptions(toast, toastConfig) { if (toast.showCloseButton === null || typeof toast.showCloseButton === 'undefined') { if (typeof toastConfig.showCloseButton === 'object') { toast.showCloseButton = toastConfig.showCloseButton[toast.type]; } else if (typeof toastConfig.showCloseButton === 'boolean') { toast.showCloseButton = toastConfig.showCloseButton; } } if (toast.showCloseButton) { toast.closeHtml = toast.closeHtml || toastConfig.closeHtml; } }, isLimitExceeded: function isLimitExceeded(toastConfig) { return toastConfig.limit && this.toasts.length > toastConfig.limit }, removeToast: function removeToast(toast) { if (toast === null || typeof toast === 'undefined') { return } var index = this.toasts.indexOf(toast); if (index < 0) { return } this.toasts.splice(index, 1); if (toast.timeoutId) { clearTimeout(toast.timeoutId); toast.timeoutId = null; } if (toast.onHideCallback) { toast.onHideCallback(toast); } }, removeToasts: function removeToasts(toastId, toastContainerId) { if (toastContainerId === null || typeof toastContainerId === 'undefined' || toastContainerId === this._toastConfig.toastContainerId) { if (toastId) { this.removeToast(this.toasts.filter(function (t) { return t.toastId === toastId; })[0]); } else { this.removeAllToasts(); } } }, removeAllToasts: function removeAllToasts() { var this$1 = this; for (var i = this.toasts.length - 1; i >= 0; i--) { this$1.removeToast(this$1.toasts[i]); } } }, computed: { _toastConfig: function _toastConfig() { return new ToastConfig(this.toastConfig) } }, beforeMount: function beforeMount() { var this$1 = this; ToastServiceBus.subscribers.push(this); ToastServiceBus.$on(ADD_TOAST, function (toast) { this$1.addToast(toast, this$1._toastConfig); }); ToastServiceBus.$on(REMOVE_TOAST, function (toastId, toastContainerId) { this$1.removeToasts(toastId, toastContainerId); }); }, render: function render(h) { return h( 'transition-group', { name: 'toast-container', tag: 'div', 'class': ['toast-container', this._toastConfig.positionClass], props: { name: this._toastConfig.animation } }, this.toasts.map(function (toast) { return (h('toast', { 'class': 'toast', props: { toast: toast }, key: toast.toastId })); }) ) } } var ToastService = { /** * Synchronously create and show a new toast instance. * * @param {(string | Toast)} type The type of the toast, or a Toast object. * @param {string=} title The toast title. * @param {string=} body The toast body. * @returns {Toast} * The newly created Toast instance with a randomly generated GUID Id. */ pop: function pop(type, title, body) { var toast = typeof type === 'string' ? { type: type, title: title, body: body } : type; if (!toast || !toast.type || !toast.type.length) { throw new Error('A toast type must be provided') } if (ToastServiceBus.subscribers.length < 1) { throw new Error( 'No Toaster Containers have been initialized to receive toasts.') } toast.toastId = Guid.newGuid(); ToastServiceBus.$emit(ADD_TOAST, toast); return toast }, /** * Removes a toast by toastId and/or toastContainerId. * * @param {string} toastId The toastId of the toast to remove. * @param {number=} toastContainerId * The toastContainerId of the container to remove toasts from. */ remove: function remove(toastId, toastContainerId) { ToastServiceBus.$emit(REMOVE_TOAST, toastId, toastContainerId); } } // http://stackoverflow.com/questions/26501688/a-typescript-guid-class var Guid = function Guid () {}; Guid.newGuid = function newGuid () { function match(c) { var r = Math.random() * 16 | 0; var v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16) } return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, match) }; var _Vue; function install(Vue) { if (install.installed) { return } install.installed = true; _Vue = Vue; installBus(_Vue); Vue.component('ToastContainer', ToastContainer); Vue.prototype.$vueOnToast = { pop: ToastService.pop, remove: ToastService.remove }; } /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) { window.Vue.use(install); } var index = { install: install, ToastService: ToastService } export default index;