Script47 / Toast

A Bootstrap 4.2+ jQuery plugin for the toast component
https://shrtn.onl/EY
MIT License
152 stars 43 forks source link

Allow use of default SVG Icons (e.g. BootStrap Icons) #31

Open slimninja opened 3 years ago

slimninja commented 3 years ago

Allow the option of switching from img to svg icons, maybe define defaults using bootstrap-icons?

        let classes = {
            header: {
                fg: '',
                bg: '',
                icon: ''
            },
            subtitle: 'text-white',
            dismiss: 'text-white'
        };
        switch (type) {
            case 'info':
                classes.header.bg = $.toastDefaults.style.info || 'bg-info';
                classes.header.fg = $.toastDefaults.style.info || 'text-white';
                classes.header.icon = `<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-exclamation-circle-fill mr-2" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                                          <path fill-rule="evenodd" d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/>
                                       </svg>`;
                break;
        html += `<div class="toast-header ${classes.header.bg} ${classes.header.fg}">`;

        html += classes.header.icon;

        html += `<strong class="mr-auto">${title}</strong>`;

image

slimninja commented 3 years ago

Made some very quick edits to work for me, hopefully a little helpful. Added the ability to use opt.img, opt.svg or default svg defined

toast.js

/**
 * @author Script47 (https://github.com/Script47/Toast)
 * @description Toast - A Bootstrap 4.2+ jQuery plugin for the toast component
 * @version 1.1.0
 **/
(function ($) {
    const TOAST_CONTAINER_HTML = `<div id="toast-container" class="toast-container" aria-live="polite" aria-atomic="true"></div>`;

    $.toastDefaults = {
        position: 'bottom-right',
        dismissible: true,
        stackable: true,
        pauseDelayOnHover: true,
        style: {
            toast: '',
            info: '',
            success: '',
            warning: '',
            error: '',
        }
    };

    $('body').on('hidden.bs.toast', '.toast', function () {
        $(this).remove();
    });

    let toastRunningCount = 1;

    function render(opts) {
        /** No container, create our own **/
        if (!$('#toast-container').length) {
            const position = ['top-right', 'top-left', 'top-center', 'bottom-right', 'bottom-left', 'bottom-center'].includes($.toastDefaults.position) ? $.toastDefaults.position : 'top-right';

            $('body').prepend(TOAST_CONTAINER_HTML);
            $('#toast-container').addClass(position);
        }

        let toastContainer = $('#toast-container');
        let html = '';
        let classes = {
            header: {
                fg: '',
                bg: '',
                icon: ''
            },
            subtitle: 'text-white',
            dismiss: 'text-white'
        };
        let id = `toast-${toastRunningCount}`;
        let type = opts.type;
        let title = opts.title;
        let subtitle = opts.subtitle;
        let content = opts.content;
        let img = opts.img;
        let svg = opts.svg;
        let delayOrAutohide = opts.delay ? `data-delay="${opts.delay}"` : `data-autohide="false"`;
        let hideAfter = ``;
        let dismissible = $.toastDefaults.dismissible;
        let globalToastStyles = $.toastDefaults.style.toast;
        let paused = false;

        if (typeof opts.dismissible !== 'undefined') {
            dismissible = opts.dismissible;
        }

        switch (type) {
            case 'info':
                classes.header.bg = $.toastDefaults.style.info || 'bg-info';
                classes.header.fg = $.toastDefaults.style.info || 'text-white';
                classes.header.icon = `<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-info-circle-fill mr-2" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                                           <path fill-rule="evenodd" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412l-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533L8.93 6.588zM8 5.5a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
                                       </svg>`;
                break;

            case 'success':
                classes.header.bg = $.toastDefaults.style.success || 'bg-success';
                classes.header.fg = $.toastDefaults.style.info || 'text-white';
                classes.header.icon = `<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-check-circle-fill mr-2" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                                           <path fill-rule="evenodd" d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
                                       </svg>`;
                break;

            case 'warning':
                classes.header.bg = $.toastDefaults.style.warning || 'bg-warning';
                classes.header.fg = $.toastDefaults.style.warning || 'text-white';
                classes.header.icon = `<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-exclamation-circle-fill mr-2" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                                          <path fill-rule="evenodd" d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/>
                                      </svg>`;
                break;

            case 'error':
                classes.header.bg = $.toastDefaults.style.error || 'bg-danger';
                classes.header.fg = $.toastDefaults.style.error || 'text-white';
                classes.header.icon = `<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-exclamation-circle-fill mr-2" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                                          <path fill-rule="evenodd" d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"/>
                                       </svg>`;
                break;
        }

        if ($.toastDefaults.pauseDelayOnHover && opts.delay) {
            delayOrAutohide = `data-autohide="false"`;
            hideAfter = `data-hide-after="${Math.floor(Date.now() / 1000) + (opts.delay / 1000)}"`;
        }

        html = `<div id="${id}" class="toast ${globalToastStyles}" role="alert" aria-live="assertive" aria-atomic="true" ${delayOrAutohide} ${hideAfter}>`;
        html += `<div class="toast-header ${classes.header.bg} ${classes.header.fg}">`;

        if (img) {
            html += `<img src="${img.src}" class="mr-2 ${img.class || ''}" alt="${img.alt || 'Image'}">`;
        } else if (svg) {
            html += svg
        } else {
            html += classes.header.icon;
        }

        html += `<strong class="mr-auto">${title}</strong>`;

        if (subtitle) {
            html += `<small class="${classes.subtitle}">${subtitle}</small>`;
        }

        if (dismissible) {
            html += `<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
                        <span aria-hidden="true" class="${classes.dismiss}">&times;</span>
                    </button>`;
        }

        html += `</div>`;

        if (content) {
            html += `<div class="toast-body">
                        ${content}
                    </div>`;
        }

        html += `</div>`;

        if (!$.toastDefaults.stackable) {
            toastContainer.find('.toast').each(function () {
                $(this).remove();
            });

            toastContainer.append(html);
            toastContainer.find('.toast:last').toast('show');
        } else {
            toastContainer.append(html);
            toastContainer.find('.toast:last').toast('show');
        }

        if ($.toastDefaults.pauseDelayOnHover) {
            setTimeout(function () {
                if (!paused) {
                    $(`#${id}`).toast('hide');
                }
            }, opts.delay);

            $('body').on('mouseover', `#${id}`, function () {
                paused = true;
            });

            $(document).on('mouseleave', '#' + id, function () {
                const current = Math.floor(Date.now() / 1000),
                    future = parseInt($(this).data('hideAfter'));

                paused = false;

                if (current >= future) {
                    $(this).toast('hide');
                }
            });
        }

        toastRunningCount++;
    }

    /**
     * Show a snack
     * @param type
     * @param title
     * @param delay
     */
    $.snack = function (type, title, delay) {
        return render({
            type,
            title,
            delay
        });
    }

    /**
     * Show a toast
     * @param opts
     */
    $.toast = function (opts) {
        return render(opts);
    }
}(jQuery));

and then I updated the toast.css to be a bit more friendly on notify:

/**
 * @author Script47 (https://github.com/Script47/Toast)
 * @description Toast - A Bootstrap 4.2+ jQuery plugin for the toast component
 * @version 1.1.0
 **/

.toast-container {
    position: fixed;
    z-index: 1055;
    margin: 1em;
}

.toast-container.top-right {
    top: 0;
    right: 0;
}

.toast-container.top-left {
    top: 0;
    left: 0;
}

.toast-container.top-center {
    transform: translateX(-50%);
    top: 0;
    left: 50%;
}

.toast-container.bottom-right {
    right: 0;
    bottom: 0;
}

.toast-container.bottom-left {
    left: 0;
    bottom: 0;
}

.toast-container.bottom-center {
    transform: translateX(-50%);
    bottom: 0;
    left: 50%;
}

.toast-container > .toast {
    min-width: 150px;
    background: transparent;
    border: none;
}

.toast-container > .toast > .toast-header {
    border: none;
}

.toast-container > .toast > .toast-header strong {
    padding-right: 20px;
}

.toast-container > .toast > .toast-body {
    background: white;
}
Script47 commented 3 years ago

@slimjim91

Thanks for taking the time out to draft some code.

I'll look further into your request/changes sometime this week and have a think about how best to implement this.

I envisage this to be v1.3.0.