Dogfalo / materialize

Materialize, a CSS Framework based on Material Design
https://materializecss.com
MIT License
38.86k stars 4.74k forks source link

Multiple toasts are displayed on the screen #4676

Open TetianaP opened 7 years ago

TetianaP commented 7 years ago

Description

Following material design guideline only one toast/snackbar may be displayed at a time. Calling toast() multiple times will result in multiple toasts appearing on the screen together.

Is there an option to show next toast message only after the previous is gone?

Repro Steps

Call Materialize.toast('I am a toast!', 4000) multiple times.

Screenshots / Codepen

image

DanielRuf commented 7 years ago

It seems there is no such option so I guess we have to find a solution (like removing old ones or stack them in a queue) and create a PR.

You can just set a callback when the toast is manually dismissed and create the next one in this case.

pmargreff commented 7 years ago

A queue looks a good way.

rcowling commented 6 years ago

is there a solution to this available? Seems as though the issue is still present.

MonizDave commented 6 years ago

Here is a basic Angular service that can be used to implement the queued system...

Basic Usgae:

this.toastService.newToastRequest({
    html: '<span class="center-align toast-center">Username cannot be left blank</span>',
    classes: 'alert alert-warning rounded'
});

The Service:

import { Injectable } from '@angular/core';

@Injectable()

export class ToastService {
    private _toast: M.Toast;
    private _requestQueue: any[];
    private _consumptionActive: boolean;

    constructor() {
        this._toast = null;
        this._requestQueue = [];
        this._consumptionActive = false;
    }

    newToastRequest(options: any, interrupt?: boolean): void {
        const optionalInterrupt: boolean = (interrupt === undefined) ? true : interrupt;
        if (!this._consumptionActive) {
            this._requestQueue.push(options);
            this.consumeNextRquest(optionalInterrupt);
        }
    }

    private consumeNextRquest(interrupt: boolean): void {
        const __this: this = this; // localize `this` for inside setTimeout as it uses the global `this`
        this._consumptionActive = true;
        if (this._toast === undefined || this._toast == null) this.showNewToast();
        else if (this._toast.timeRemaining > 0 && interrupt) this._toast.dismiss();
        else if (this._toast.timeRemaining > 0 && !interrupt) {
            setTimeout(function () {
                __this._toast.dismiss();
            }, __this._toast.timeRemaining);
        }
    }

    private constructOptionsWithDefaults(options: any): M.ToastOptions {
        const __this: this = this; // localize `this` for inside completeCallback as it uses the global `this`
        const completeOptions: M.ToastOptions = { // initialize options with defaults, overwrite with submitted values
            html: '',
            displayLength: 4000,
            inDuration: 300,
            outDuration: 375,
            classes: '',
            activationPercent: 0.8,
            completeCallback: null
        };
        if (options.html !== undefined && options.html != null) completeOptions.html = options.html;
        if (options.displayLength !== undefined && options.displayLength != null) completeOptions.displayLength = options.displayLength;
        if (options.inDuration !== undefined && options.inDuration != null) completeOptions.inDuration = options.inDuration;
        if (options.outDuration !== undefined && options.outDuration != null) completeOptions.html = options.outDuration;
        if (options.classes !== undefined && options.classes != null) completeOptions.classes = options.classes;
        if (options.activationPercent !== undefined && options.activationPercent != null) completeOptions.activationPercent = options.activationPercent;
        completeOptions.completeCallback = function () {
            if (options.completeCallback !== undefined && options.completeCallback != null) options.completeCallback();
            if (__this._requestQueue.length === 0) __this._toast = null;
            __this.showNewToast();
        };
        return completeOptions;
    }

    private showNewToast(): void {
        const __this: this = this; // localize `this` for inside setTimeout as it uses the global `this`
        let currentRequest: M.ToastOptions = null;
        if (this._requestQueue.length > 0) {
            currentRequest = this._requestQueue.shift();
            this._toast = new M.Toast(this.constructOptionsWithDefaults(currentRequest));
            setTimeout(function() { // timeout to help block rapid requests
                __this._consumptionActive = false;
            }, __this._toast.options.inDuration);
        }
    }
}