/** * A 'Toast' is a simple message that is displayed on the screen and then automatically closed by a * timeout. Think about it like a text only alert box that will self destruct. **A Toast should not * be instantiated manually** but creating by calling 'Ext.toast(message, timeout)'. This will create * one reusable toast container and content will be swapped out as toast messages are queued * or displayed. * * # Simple Toast * * @example * // Toast will close in 1000 milliseconds (default) * Ext.toast('Hello Sencha!'); * * # Toast with Timeout * * @example * // Toast will close in 5000 milliseconds * Ext.toast('Hello Sencha!', 5000); * * # Toast with config * * @example * // Toast will close in 2000 milliseconds * Ext.toast({message: 'Hello Sencha!', timeout: 2000}); * * # Multiple Toasts queued * * @example * Ext.toast('Hello Sencha!'); * Ext.toast('Hello Sencha Again!'); * Ext.toast('Hello Sencha One More Time!'); */Ext.define('Ext.Toast', { extend: 'Ext.Sheet', requires: [ 'Ext.util.InputBlocker' ], config: { /** * @cfg centered * @inheritdoc */ centered: false, /** * @cfg showAnimation * @inheritdoc */ showAnimation: { type: 'popIn', duration: 250, easing: 'ease-out' }, /** * @cfg hideAnimation * @inheritdoc */ hideAnimation: { type: 'popOut', duration: 250, easing: 'ease-out' }, /** * Override the default `zIndex` so it is normally always above positioned components. */ zIndex: 999, /** * @cfg {String} message * The message to be displayed in the {@link Ext.Toast}. * @accessor */ message: '', /** * @cfg {Number} timeout * The amount of time in milliseconds to wait before destroying the toast automatically */ timeout: 1000, /** * @cfg {Number} maxQueue * The the maximum number of toasts that can be queued up. When there are this many toasts queued up and * a new call to Ext.toast() is made, the oldest queued Toast that is not yet displayed will be dropped * from the queue. */ maxQueue: 3, /** * @cfg {Boolean/Object} messageAnimation * The animation that should be used between toast messages when they are queued up */ messageAnimation: true, /** * @cfg hideOnMaskTap * @inheritdoc */ hideOnMaskTap: true, /** * @cfg modal * @hide */ modal: false, /** * @cfg layout * @inheritdoc */ layout: { type: 'vbox', pack: 'center' } }, /** * @property classCls * @inheritdoc */ classCls: Ext.baseCSSPrefix + 'toast', /** * @private */ applyMessage: function (value) { var config = { html: value, cls: this.baseCls + '-text' }; return Ext.factory(config, Ext.Component, this._message); }, /** * @private */ updateMessage: function (newMessage) { if (newMessage) { this.add(newMessage); } }, /** * @private */ startTimer: function () { var timeout = this.getTimeout(); if (this._timeoutID) { Ext.undefer(this._timeoutID); } if (!Ext.isEmpty(timeout)) { this._timeoutID = Ext.defer(this.onTimeout.bind(this), timeout); } else { this.onTimeout(); } }, stopTimer: function () { Ext.undefer(this._timeoutID); this._timeoutID = null; }, /** * @method * @private */ next: Ext.emptyFn, getIsAnimating: function () { var messageContainer = this.getMessage(); return (messageContainer && Ext.Animator.hasRunningAnimations(messageContainer)) || Ext.Animator.hasRunningAnimations(this); }, /** * @private */ showToast: function (config) { var me = this, message = config.message, timeout = config.timeout, messageContainer = me.getMessage(), msgAnimation = me.getMessageAnimation(); // If the toast has already been rendered and is visible on the screen if (me.isRendered() && me.isHidden() === false) { messageContainer.onAfter({ // After the hide is complete hiddenchange: function () { me.setMessage(message); me.setTimeout(timeout); messageContainer.onAfter({ scope: me, // After the show is complete hiddenchange: function () { me.startTimer(); }, single: true }); messageContainer.show(msgAnimation); }, scope: me, single: true }); messageContainer.hide(msgAnimation); } else { Ext.util.InputBlocker.blockInputs(); //if it has not been added to a container, add it to the Viewport. if (!me.getParent() && Ext.Viewport) { Ext.Viewport.add(me); } me.setMessage(message); me.setTimeout(timeout); me.startTimer(); me.show({ animation: null, alignment: { component: document.body, alignment: 't-t', options: { offset: [0, 20] } } }); } }, /** * @private */ beforeHide: function (animation) { this.callParent(arguments); // If the message is animating cancel this hide if (this.getIsAnimating()) { return false; } this.stopTimer(); if (!this.next()) { return false; } }, /** * @private */ onTimeout: function () { if (this._timeoutID !== null) { this.hide(); } }, doDestroy: function() { this.stopTimer(); this.callParent(); }}, function (Toast) { var _queue = []; function getInstance() { if (!Ext.Toast._instance) { Ext.Toast._instance = Ext.create('Ext.Toast'); } return Ext.Toast._instance; } //<debug> /** * @member Ext.Toast * @method getQueueCount * @private * Provided for unit tests * @returns {Number} */ Toast.prototype.getQueueCount = function() { return _queue.length; }; //</debug> Toast.prototype.next = function () { var config = _queue.shift(); if (config) { this.showToast(config); } return !config; }; /** * Destroys any Toast components and elements, freeing the resources. * * They will be created again upon calling Ext.toast(). */ Ext.Toast.destroy = function() { if (Ext.Toast._instance) { Ext.Toast._instance.destroy(); Ext.Toast._instance = null; } }; Ext.toast = function (message, timeout) { var toast = getInstance(), maxQueue = Ext.Toast.prototype.config.maxQueue, config = message; if (Ext.isString(message)) { config = { message: message, timeout: timeout }; } //<debug> if (!config) { throw new Error("Toast requires a message"); } //</debug> if (config.timeout === undefined) { config.timeout = Ext.Toast.prototype.config.timeout; } _queue.push(config); if (_queue.length > maxQueue) { _queue.shift(); } if (!toast.isRendered() || toast.isHidden()) { toast.next(); } return toast; }});