light-toast
Version:
A light-weight react toast component built for mobile web app
258 lines (240 loc) • 12.3 kB
JavaScript
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = ".style_mask__9zmSN{position:fixed;top:0;right:0;bottom:0;left:0;display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;background:transparent;z-index:1999}.style_box__2iia4{display:inline-block;max-width:85%;min-width:95px;padding:9px 15px;box-sizing:border-box;text-align:center;word-break:break-all;white-space:pre-wrap;font-size:0;color:#fff;background-color:rgba(58,58,58,.9);border-radius:3px;opacity:0;-webkit-transform:scale(.9);transform:scale(.9);-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:opacity .3s,-webkit-transform .3s;transition:opacity .3s,transform .3s;transition:opacity .3s,transform .3s,-webkit-transform .3s}.style_enter__29LyQ{opacity:1;-webkit-transform:scale(1);transform:scale(1)}.style_exit__3WYmp{opacity:0;-webkit-transform:scale(.9);transform:scale(.9)}.style_message__1DUXc{line-height:1.5;font-size:14px}.style_wrapper__cQFyX{margin:0 auto 7px;width:36px;height:36px}.style_loading__2pb7J{-webkit-animation:style_loading__2pb7J 1s linear infinite;animation:style_loading__2pb7J 1s linear infinite}@-webkit-keyframes style_loading__2pb7J{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes style_loading__2pb7J{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}";
var styles = {"mask":"style_mask__9zmSN","box":"style_box__2iia4","enter":"style_enter__29LyQ","exit":"style_exit__3WYmp","message":"style_message__1DUXc","wrapper":"style_wrapper__cQFyX","loading":"style_loading__2pb7J"};
styleInject(css_248z);
var Icon = function (_a) {
var type = _a.type;
switch (type) {
case 'success':
return (React.createElement("svg", { viewBox: "0 0 1024 1024", fill: "#fff" },
React.createElement("path", { d: "M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0 0 51.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z" }),
React.createElement("path", { d: "M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" })));
case 'fail':
return (React.createElement("svg", { viewBox: "0 0 1024 1024", fill: "#fff" },
React.createElement("path", { d: "M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 0 0-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z" }),
React.createElement("path", { d: "M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z" })));
case 'loading':
return (React.createElement("svg", { viewBox: "0 0 1024 1024", fill: "#fff", className: styles.loading },
React.createElement("path", { d: "M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z" })));
}
};
var EventManager = /** @class */ (function () {
function EventManager() {
this._events = {};
this._publishedEvents = [];
this._uniqueId = 0; // use self increasing number to guarantee global uniqueness
}
EventManager.prototype.subscribe = function (name, callback) {
if (!this._events[name]) {
this._events[name] = [];
}
var event = { key: this._uniqueId++, callback: callback };
this._events[name].push(event);
return event.key;
};
EventManager.prototype.publish = function (name, data) {
this._publishedEvents.push({ name: name, data: data });
if (this._events[name]) {
this._events[name].forEach(function (_a) {
var callback = _a.callback;
callback(data);
});
}
};
EventManager.prototype.unSubscribe = function (name, key) {
if (this._events[name]) {
for (var i = 0; i < this._events[name].length; i++) {
if (key === this._events[name][i].key) {
this._events[name].splice(i, 1);
break;
}
}
}
};
// Make sure published events can be triggered when subscribed
EventManager.prototype.ensureTriggeredAndSubscribe = function (name, callback) {
var event = this._publishedEvents
.slice()
.reverse()
.find(function (item) { return item.name === name; });
if (event) {
callback(event.data);
}
return this.subscribe(name, callback);
};
return EventManager;
}());
var eventManager = new EventManager();
var Toast = function (_a) {
var id = _a.id, type = _a.type, content = _a.content, _b = _a.duration, duration = _b === void 0 ? 3000 : _b, onClose = _a.onClose;
var _c = useState(styles.box), classes = _c[0], setClasses = _c[1];
var _d = useState(false), entered = _d[0], setEntered = _d[1];
var ref = useRef(null);
function exit() {
setClasses(function () { return styles.box + " " + styles.exit; });
}
useEffect(function () {
// force a repaint
// eslint-disable-next-line
ref.current && ref.current.scrollTop;
setClasses(function (prev) { return prev + " " + styles.enter; });
}, []);
useEffect(function () {
var key = -1;
var timerId = -1;
if (entered) {
// component mounting is async, there might be an exit command before a toast mounts
// so we should make sure to trigger the published exit event
key = eventManager.ensureTriggeredAndSubscribe('lt#exit', function (messageId) {
if (messageId === id) {
exit();
}
});
if (duration !== 0) {
timerId = window.setTimeout(exit, duration);
}
}
return function () {
eventManager.unSubscribe('lt#exit', key);
window.clearTimeout(timerId);
};
}, [id, duration, entered]);
return (React.createElement("div", { className: styles.mask },
React.createElement("div", { className: classes, style: type === 'info' ? undefined : { padding: 15, borderRadius: 5 }, onTransitionEnd: function () {
// enter phase
if (~classes.indexOf(styles.enter)) {
setEntered(true);
}
// exit phase
if (~classes.indexOf(styles.exit)) {
onClose();
}
}, ref: ref },
type !== 'info' && (React.createElement("div", { className: styles.wrapper, style: type === 'loading' ? { marginBottom: 10 } : undefined },
React.createElement(Icon, { type: type }))),
React.createElement("span", { className: styles.message }, content))));
};
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
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);
};
/**
* Message queue.
* Save messages in a queue, only remove it when component lifecycle ends.
*/
var Queue = /** @class */ (function () {
function Queue() {
this._messages = [];
this._uniqueId = 0;
}
Queue.prototype.push = function (message) {
this._messages.push(__assign({ id: this._uniqueId++ }, message));
};
Object.defineProperty(Queue.prototype, "length", {
get: function () {
return this._messages.length;
},
enumerable: false,
configurable: true
});
Queue.prototype.getFirstMessage = function () {
return this._messages[0];
};
Queue.prototype.shift = function () {
return this._messages.shift();
};
return Queue;
}());
var queue = new Queue();
eventManager.subscribe('lt#popmessage', function (_a) {
var id = _a.id, type = _a.type, content = _a.content, duration = _a.duration, onClose = _a.onClose;
var container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(React.createElement(Toast, { id: id, type: type, content: content, duration: duration, onClose: function () {
ReactDOM.unmountComponentAtNode(container);
document.body.removeChild(container);
onClose && onClose();
queue.shift();
if (queue.length > 0) {
eventManager.publish('lt#popmessage', queue.getFirstMessage());
}
} }), container);
});
function notice(type, content, duration, onClose) {
queue.push({ type: type, content: content, duration: duration, onClose: onClose });
// toast right now if there is only one message in queue
if (queue.length === 1) {
eventManager.publish('lt#popmessage', queue.getFirstMessage());
return;
}
// if current message is loading, then we should unmount it to proceed
if (queue.length > 1) {
var message = queue.getFirstMessage();
if (message.type === 'loading') {
eventManager.publish('lt#exit', message.id);
}
}
}
var index = {
info: function (content, duration, onClose) {
notice('info', content, duration, onClose);
},
success: function (content, duration, onClose) {
notice('success', content, duration, onClose);
},
fail: function (content, duration, onClose) {
notice('fail', content, duration, onClose);
},
loading: function (content, onClose) {
notice('loading', content, 0, onClose);
},
hide: function () {
// hide the first toast in the queue when executing the command
if (queue.length > 0) {
eventManager.publish('lt#exit', queue.getFirstMessage().id);
}
},
};
export default index;
//# sourceMappingURL=index.es.js.map