UNPKG

notyf

Version:

A dead simple, responsive, a11y, dependency-free, vanilla JavaScript toast library.

332 lines (321 loc) 13.5 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var NotyfNotification = /** @class */ (function () { function NotyfNotification(options) { this.options = options; } return NotyfNotification; }()); (function (NotyfArrayEvent) { NotyfArrayEvent[NotyfArrayEvent["Add"] = 0] = "Add"; NotyfArrayEvent[NotyfArrayEvent["Remove"] = 1] = "Remove"; })(exports.NotyfArrayEvent || (exports.NotyfArrayEvent = {})); var NotyfArray = /** @class */ (function () { function NotyfArray() { this.notifications = []; } NotyfArray.prototype.push = function (elem) { this.notifications.push(elem); this.updateFn(elem, exports.NotyfArrayEvent.Add, this.notifications); }; NotyfArray.prototype.splice = function (index, num) { var elem = this.notifications.splice(index, num)[0]; this.updateFn(elem, exports.NotyfArrayEvent.Remove, this.notifications); }; NotyfArray.prototype.indexOf = function (elem) { return this.notifications.indexOf(elem); }; NotyfArray.prototype.onupdate = function (fn) { this.updateFn = fn; }; return NotyfArray; }()); var DEFAULT_OPTIONS = { types: [ { type: 'success', className: 'notyf__toast--success', backgroundColor: '#3dc763', icon: { className: 'notyf__icon--success', tagName: 'i', }, }, { type: 'error', className: 'notyf__toast--error', backgroundColor: '#ed3d3d', icon: { className: 'notyf__icon--error', tagName: 'i', }, }, ], duration: 2000, ripple: true, }; var NotyfView = /** @class */ (function () { function NotyfView() { this.notifications = []; // Creates the main notifications container var docFrag = document.createDocumentFragment(); var notyfContainer = this._createHTLMElement({ tagName: 'div', className: 'notyf' }); docFrag.appendChild(notyfContainer); document.body.appendChild(docFrag); this.container = notyfContainer; // Identifies the main animation end event this.animationEndEventName = this._getAnimationEndEventName(); this._createA11yContainer(); } NotyfView.prototype.update = function (notification, type) { if (type === exports.NotyfArrayEvent.Add) { this.addNotification(notification); } else if (type === exports.NotyfArrayEvent.Remove) { this.removeNotification(notification); } }; NotyfView.prototype.removeNotification = function (notification) { var _this = this; var renderedNotification = this._popRenderedNotification(notification); var node; if (!renderedNotification) { return; } node = renderedNotification.node; node.classList.add('notyf__toast--disappear'); var handleEvent; node.addEventListener(this.animationEndEventName, handleEvent = function (event) { if (event.target === node) { node.removeEventListener(_this.animationEndEventName, handleEvent); _this.container.removeChild(node); } }); }; NotyfView.prototype.addNotification = function (notification) { var node = this._renderNotification(notification); this.notifications.push({ notification: notification, node: node }); // For a11y purposes, we still want to announce that there's a notification in the screen // even if it comes with no message. this._announce(notification.options.message || 'Notification'); }; NotyfView.prototype._renderNotification = function (notification) { var card = this._buildNotificationCard(notification); var className = notification.options.className; if (className) { card.classList.add(className); } this.container.appendChild(card); return card; }; NotyfView.prototype._popRenderedNotification = function (notification) { var idx = -1; for (var i = 0; i < this.notifications.length && idx < 0; i++) { if (this.notifications[i].notification === notification) { idx = i; } } if (idx !== -1) { return this.notifications.splice(idx, 1)[0]; } return; }; NotyfView.prototype._buildNotificationCard = function (notification) { var options = notification.options; var iconOpts = options.icon; // Create elements var notificationElem = this._createHTLMElement({ tagName: 'div', className: 'notyf__toast' }); var ripple = this._createHTLMElement({ tagName: 'div', className: 'notyf__ripple' }); var wrapper = this._createHTLMElement({ tagName: 'div', className: 'notyf__wrapper' }); var message = this._createHTLMElement({ tagName: 'div', className: 'notyf__message' }); message.innerHTML = options.message || ''; var color = options.backgroundColor; // build the icon and append it to the card if (iconOpts && typeof iconOpts === 'object') { var iconContainer = this._createHTLMElement({ tagName: 'div', className: 'notyf__icon' }); var icon = this._createHTLMElement({ tagName: iconOpts.tagName || 'i', className: iconOpts.className, text: iconOpts.text, }); if (color) { icon.style.color = color; } iconContainer.appendChild(icon); wrapper.appendChild(iconContainer); } wrapper.appendChild(message); notificationElem.appendChild(wrapper); // add ripple if applicable, else just paint the full toast if (color) { if (options.ripple) { ripple.style.backgroundColor = color; notificationElem.appendChild(ripple); } else { notificationElem.style.backgroundColor = color; } } return notificationElem; }; NotyfView.prototype._createHTLMElement = function (_a) { var tagName = _a.tagName, className = _a.className, text = _a.text; var elem = document.createElement(tagName); if (className) { elem.className = className; } elem.textContent = text || null; return elem; }; /** * Creates an invisible container which will announce the notyfs to * screen readers */ NotyfView.prototype._createA11yContainer = function () { var a11yContainer = this._createHTLMElement({ tagName: 'div', className: 'notyf-announcer' }); a11yContainer.setAttribute('aria-atomic', 'true'); a11yContainer.setAttribute('aria-live', 'polite'); // Set the a11y container to be visible hidden. Can't use display: none as // screen readers won't read it. a11yContainer.style.border = '0'; a11yContainer.style.clip = 'rect(0 0 0 0)'; a11yContainer.style.height = '1px'; a11yContainer.style.margin = '-1px'; a11yContainer.style.overflow = 'hidden'; a11yContainer.style.padding = '0'; a11yContainer.style.position = 'absolute'; a11yContainer.style.width = '1px'; a11yContainer.style.outline = '0'; document.body.appendChild(a11yContainer); this.a11yContainer = a11yContainer; }; /** * Announces a message to screenreaders. */ NotyfView.prototype._announce = function (message) { var _this = this; this.a11yContainer.textContent = ''; // This 100ms timeout is necessary for some browser + screen-reader combinations: // - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout. // - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a // second time without clearing and then using a non-zero delay. // (using JAWS 17 at time of this writing). // https://github.com/angular/material2/blob/master/src/cdk/a11y/live-announcer/live-announcer.ts setTimeout(function () { _this.a11yContainer.textContent = message; }, 100); }; /** * Determine which animationend event is supported */ NotyfView.prototype._getAnimationEndEventName = function () { var el = document.createElement('_fake'); var transitions = { MozTransition: 'animationend', OTransition: 'oAnimationEnd', WebkitTransition: 'webkitAnimationEnd', transition: 'animationend', }; var t; for (t in transitions) { if (el.style[t] !== undefined) { return transitions[t]; } } // No supported animation end event. Using "animationend" as a fallback return 'animationend'; }; return NotyfView; }()); /** * Main controller class. Defines the main Notyf API. */ var Notyf = /** @class */ (function () { function Notyf(opts) { var _this = this; this.notifications = new NotyfArray(); this.view = new NotyfView(); var types = this.registerTypes(opts); this.options = __assign({}, DEFAULT_OPTIONS, opts); this.options.types = types; this.notifications.onupdate(function (elem, type) { _this.view.update(elem, type); }); } Notyf.prototype.error = function (payload) { var options = this.normalizeOptions('error', payload); this.open(options); }; Notyf.prototype.success = function (payload) { var options = this.normalizeOptions('success', payload); this.open(options); }; Notyf.prototype.open = function (options) { var defaultOpts = this.options.types.find(function (_a) { var type = _a.type; return type === options.type; }) || {}; var config = __assign({}, defaultOpts, options); config.ripple = config.ripple === undefined ? this.options.ripple : config.ripple; var notification = new NotyfNotification(config); this._pushNotification(notification); }; Notyf.prototype._pushNotification = function (notification) { var _this = this; this.notifications.push(notification); var duration = notification.options.duration || this.options.duration; setTimeout(function () { var index = _this.notifications.indexOf(notification); _this.notifications.splice(index, 1); }, duration); }; Notyf.prototype.normalizeOptions = function (type, payload) { var options = { type: type }; if (typeof payload === 'string') { options.message = payload; } else if (typeof payload === 'object') { options = __assign({}, options, payload); } return options; }; Notyf.prototype.registerTypes = function (opts) { var incomingTypes = (opts && opts.types || []).slice(); var finalTypes = DEFAULT_OPTIONS.types.map(function (defaultType) { // find if there's a default type within the user input's types, if so, it means the user // wants to change some of the default settings var userTypeIdx = incomingTypes.findIndex(function (t) { return t.type === defaultType.type; }); var userType = userTypeIdx !== -1 ? incomingTypes.splice(userTypeIdx, 1)[0] : {}; return __assign({}, defaultType, userType); }); return finalTypes.concat(incomingTypes); }; return Notyf; }()); exports.Notyf = Notyf; exports.NotyfNotification = NotyfNotification; exports.NotyfArray = NotyfArray; exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS; exports.NotyfView = NotyfView;