Toast - всплывающие сообщения

Javascript, jComponent 12 января 2020 4 мин. 5987


Toast или всплывающие сообщения - это плагин для сайтов и веб-приложений. Основная цель которого заключается в том, чтобы показать конечному пользователю уведомление заметным образом, которые не мешают его работе и в то же время держат его в курсе.

Toast notify
Пример всплывающих сообщений Toast

Подобные плагины я использовал постояно, но когда стал использовать библиотеку jComponent появилось желание сделать подобный компонент, но с учётом своих потребностей, а также оптимизировать его работу. Поэтому на свет появился этот компонент, который очень легко использовать и настроить под свои задачи.

В данном посте я не буду рассказывать как я его написал, у кого есть желание может взгялнуть на исходник и все разобрать. В этом посте я расскажу как его использовать и опишу основной функционал.

Возможности:

  • Типы сообщений - уведомления каждого типа выводится определенным цветом
  • Время жизни уведомления - сообщение будет висеть до окончания времени жизни
  • Миниатюры - можно использовать изображения в уведомлениях
  • Кэллбэк - есть возможность получить уведомление о закрытие уведомления
  • Кастомизация иконок - можно менять иконки в зависимости от ситуации и типа сообщения
  • Местоположение - область где будет выведено сообщение
  • Время и дата - вывод времени в уведомлении
  • Анимация - можно задать определенный стиль анимации при отображении всплывающего сообщения

Подключение

Сперва необходимо подключить библиотеку jComponent. Как это сделать можно посмотреть этот пост jComponent - #Часть 1, Подключение, DataBinding.

После чего к своим компонентам, добавить компонент toast. Исходник которого приведен ниже.

COMPONENT('toast', 'timeout:8; position:top-right; loader:true; animate:fade', function(self, config) {        
    self.singleton();
    self.readonly();  
    self.template = Tangular.compile('<div class="ui-toast {{ type }}" data-id="{{ id }}" {{ if callback }} style="cursor:pointer"{{ fi }}>{{ if loader }}<div class="loader"></div>{{fi}}<i class="fa fa-times"></i><div class="ui-toast-icon">{{if icon }}<i class="fa {{ icon }}"></i>{{fi}}{{if img }}{{ img | raw }}{{fi}}</div><div class="ui-toast-message">{{if date }}<div class="ui-toast-datetime">{{ date }}</div>{{fi}}{{ mess | raw }}</div></div>');
    self.items = {};
    self.make = function() {
        self.aclass('ui-toast-container');
        let position = config.position || 'top-right';        
        self.aclass(position);     

        self.event('click', 'a,button', function(e) {
            e.stopPropagation();
        });

        self.event('click', '.ui-toast', function() {
            var el = $(this);
            var id = el.attr('data-id');            
            var obj = self.items[id];            
            self.close(obj.id);
        });
    };

    self.configure = function(key, value, init, prev) {
        if (init)
            return;
        if (key=='position') {
            self.rclass();
            self.aclass('ui-toast-container '+value);
        }        
    }

    self.close = function(id) {
        var obj = self.items[id];          
        if (obj.autoClose) clearTimeout(obj.autoClose);
        if (!obj) return;
        if (obj.callback) obj.callback(obj);
        obj.callback = null;
        delete self.items[id];        
        if (config.animate == 'fade') {
            self.find('div[data-id="{0}"]'.format(id)).fadeOut('normal', function() { $(this).remove()});
        } else if (config.animate == 'slide') {
            self.find('div[data-id="{0}"]'.format(id)).slideUp('normal', function() { $(this).remove()});    
        }
          else {
            self.find('div[data-id="{0}"]'.format(id)).remove();
        }        
    };

    self.success = function(mess, o, callback) {                
        self.append(mess, o, callback||null, 'success', 'check');
    };    
    self.warning = function(mess, o, callback) {                
        self.append(mess, o, callback||null, 'warning', 'exclamation-triangle');
    };    
    self.error = function(mess, o, callback) {                        
        self.append(mess, o, callback||null, 'error', 'bell');
    };    
    self.info = function(mess, o, callback) {                
        self.append(mess, o, callback||null, 'info', 'info-circle');
    }; 

    self.append = function(mess, o, callback, tp, ic) { 
        console.log(config);
        if (typeof(o) === 'function') {
            callback = o;
            o = null;
        }
        if (!o) o = {};
        o.type = o.type || tp || null;
        o.icon = o.icon || ic || null;
        let id = o.id||Math.floor(Math.random() * 100000);
        let type = (o.type) ? o.type : '';
        let icon = (o.icon) ? 'fa-' + o.icon : null;
        let img = (o.img) ? "<img class='img-rounded img-responsive' src='" + o.img + "'>" : null;
        let date = (o.date) ? o.date.format(config.format) : (config.dateAlways) ? new Date().format(config.format): null;       

        var obj = { id:id, type:type, icon:icon, img:img, mess:mess, date:date, callback: callback }; 
        obj.timeout = o.timeout || config.timeout;
        if (obj.timeout) obj.timeout *= 1000;
        obj.loader = o.loader || config.loader;         
        self.items[obj.id] = obj;
        var elem = self.template(obj);
        self.element.append(elem);

        if (config.animate == 'fade') {        
          self.element.find('.ui-toast:last').hide().fadeIn();                
        } else if (config.animate == 'slide') {
            self.element.find('.ui-toast:last').hide().slideDown();                            
        }      
        if (obj.loader) {
            self.updateLoader(obj);            
        }
        if (obj.timeout) self.autoclose(obj);
    };

    self.updateLoader = function(obj) {                        
        var el = self.find('.ui-toast[data-id="'+obj.id+'"] .loader');
        var transitionTime = (obj.timeout/1000)+'s';        
        var style = '';
        style += '-webkit-transition: width ' + transitionTime + ' ease-in; \
                  -o-transition: width ' + transitionTime + ' ease-in; \
                  transition: width ' + transitionTime + ' ease-in; \
                  background-color: #000; \
                  opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40);';
        el.attr('style', style);          
        setTimeout(function() {el.aclass('loaded'); }, 300);        
    };    

    self.autoclose = function(obj) {
        obj.autoClose = setTimeout(function() {                                    
            self.close(obj.id);            
        }, obj.timeout);        
    };
})

CSS стили для данного компонента.

/* Toast */
.ui-toast-container { position: fixed; z-index: 10000; padding: 5px 5px 0 0;}
.ui-toast-container.top-center{top:0;right:0;width:100%}
.ui-toast-container.bottom-center{bottom:0;right:0;width:100%}
.ui-toast-container.top-full-width{top:0;right:0;width:100%}
.ui-toast-container.bottom-full-width{bottom:0;right:0;width:100%}
.ui-toast-container.top-left{top:12px;left:12px}
.ui-toast-container.top-right{top:12px;right:12px}
.ui-toast-container.bottom-right{right:12px;bottom:12px}
.ui-toast-container.bottom-left{bottom:12px;left:12px}
.ui-toast-container.bottom-center>div,.ui-toast-container.top-center>div{width:300px;margin-left:auto;margin-right:auto}
.ui-toast-container.bottom-full-width>div,.ui-toast-container.top-full-width>div{width:96%;margin-left:auto;margin-right:auto}
.ui-toast-container>div {width:300px;}
.ui-toast { background-color: white; box-shadow: 0 0 3px #999; border-radius: 3px; font-size: 14px; padding: 15px 15px 15px 10px; margin: 0 0 6px; position: relative; display: flex; opacity:.9;}
.ui-toast .loader{  display: block; width: 0%; position:absolute;left:0;bottom:0;height:4px;/*background-color:#000;opacity:.4;-ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40);filter:alpha(opacity=40)*/}
.ui-toast .loaded { width: 100%; }
.ui-toast:hover { -moz-box-shadow: 0 0 4px #999; -webkit-box-shadow: 0 0 4px #999; box-shadow: 0 0 4px #999; opacity: 1; -ms-filter: alpha(opacity=100); filter: alpha(opacity=100); }
.ui-toast > i { position: absolute; right: 12px; top: 10px; color: #AAB2BD; font-size: 14px; cursor: pointer; text-shadow:0 1px 0 #fff; opacity:.8; }
.ui-toast > i:active { top: 11px; }
.ui-toast > i:hover { color:#000; text-decoration:none; cursor:pointer; opacity:.4; -ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40); filter:alpha(opacity=40) }
.ui-toast-icon{ float: left; font-size: 24px; color: #AAB2BD; line-height: 24px; /*padding-right:0.5em;*/ margin: auto 0.1em; }
.ui-toast-message{ margin: 1px 0 0 10px; color: gray; max-height: 150px; }
.ui-toast-datetime{ font-size: 10px; color: #A0A0A0; margin-bottom: 4px; padding-top: 5px; }
.ui-toast.success{ background-color: #1ab394;}
.ui-toast.error{ background-color: #ed5565;}
.ui-toast.info{ background-color: #2f96b4;}
.ui-toast.warning{ background-color: #F89406;}
.ui-toast.success > i, .ui-toast.error > i, .ui-toast.info > i, .ui-toast.warning > i { color: #fff; }
.ui-toast.success > i:hover, .ui-toast.error > i:hover, .ui-toast.info > i:hover, .ui-toast.warning > i:hover { color:#000; text-decoration:none; opacity:.4; -ms-filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=40); filter:alpha(opacity=40) }
.ui-toast.success .ui-toast-icon, .ui-toast.error .ui-toast-icon, .ui-toast.info .ui-toast-icon, .ui-toast.warning .ui-toast-icon { color: #fff; }
.ui-toast.success .ui-toast-message, .ui-toast.error .ui-toast-message, .ui-toast.info .ui-toast-message, .ui-toast.warning .ui-toast-message { color:  #fff;  }
.ui-toast.success .ui-toast-datetime, .ui-toast.error .ui-toast-datetime, .ui-toast.info .ui-toast-datetime, .ui-toast.warning .ui-toast-datetime { color: #F5F5F5; } { color: #E0E0E0; }

Как использовать

В HTML код необходимо разместить тэг с описанием компонента и его параметрами:

<div data-jc="toast" data-jc-config='timeout:6;format:dd\.MM\.yyyy hh\:mm;animate:fade;'></div>
<!-- или короткое описание компонента -->
<div data---="toast__null__timeout:6;format:dd\.MM\.yyyy hh\:mm;animate:fade;"></div>

В параметрах можно задать следующие свойства по умолчанию:

  • animate - стиль анимации при выводе и скрытии сообщения, возможные варианты fade и slide
  • timeout - время жизни сообщения в секундах
  • format - формат даты и времени в сообщении
  • loader - прогресс бар, true - выводится, false - скрыт. По умолчанию true.
  • position - позиционирование, место для отображения на экране, возможные значение top-center, bottom-center, top-full-width, bottom-full-width, top-left, top-right, bottom-right, bottom-left, bottom-center, bottom-full-width. По умолчанию top-right.

Для удобства при работе с плагином, необходимо его инициализировать.

var toast;
FIND('toast', (comp) => toast = comp);                     

Методы

  • toast.success(message, options, callback)
  • toast.warning(message, options, callback)
  • toast.error(message, options, callback)
  • toast.info(message, options, callback)

Данные методы выводят сообщение определенного типа, уведомления будет с соответствующим цветом и иконкой. Параметры: message - сообщение (String), options - дополнительные параметры (Object), callback - кэллбэк, функция, которая будет вызвана при закрытии уведомления.

toast.reconfigure(options); - метод позволяет добавить или изменить, параметры компонента, которые были заданы по умолчанию.


Примеры

//сообщение об успехе
toast.success('<b>Title</b><br>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod');
//сообщение об ошибке, используем форматирование для вывода сообщения
toast.error('<b>{0}</b><br>{1}<br><a href="#" class="btn btn-xs btn-default">Link</a>'.format('Title', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'));
//информационное сообщение, к сообщение прикрпим аватар и увеличим время жизни
toast.info('<b>Title</b><br>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod', {'img': 'https://dummyimage.com/80x80/000/fff.jpg', 'timeout': 10});
//уведомление о проблеме, изменим иконку, выведем время и дату проблемы и увеличим время жизни
toast.warning('<b>Title</b><br>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod', {'icon': 'battery', 'date': NOW.add('5 day'), 'timeout': 10});
//изменим местоположение вывода сообщений, будем выводить слева внизу
toast.reconfigure({ position: 'bottom-left' });
//сообщение об ошибке с соответсвущей иконкой, также используем коллбэк, чтобы понять когда было закрыто уведомление
toast.error('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod', {'icon': 'battery-empty'}, function(res){       
        alert('Close notify - '+res.mess);
});

Также я сделал, демо версию, где используются все возможности данного компонента.

See the Pen Toast - notify popup by Saper639 (@saper639) on CodePen.

Заключение

Cчитаю, получился добротный компонент, функционал которого можно расширить. Я даже сравнивал объем исходника своего компонента, с подобными для других фреймворков на Vue, Angular и я сделал вывод, что у меня получился самый минимальный.

В дальнейшем планирую добавить, звуковое сопровождение, то есть при отображении тревожного сообщения включать сирену, может ещё что-то. Пишите комментируйте, что ещё добавить, удобен ли Вам данный компонент. На Ваши вопросы и комменты я обязательно отвечу.


Ссылки:

Категории