jupyterlab_toastify
Version:
Integrate 'react-toastify' nicely in JupyterLab.
244 lines (243 loc) • 10.4 kB
JavaScript
/* eslint-disable no-inner-declarations */
/* eslint-disable react/jsx-key */
/* eslint-disable react/prop-types */
import { faBell } from '@fortawesome/free-solid-svg-icons/faBell';
import { faCheck } from '@fortawesome/free-solid-svg-icons/faCheck';
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons/faExclamationCircle';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
import { faSpinner } from '@fortawesome/free-solid-svg-icons/faSpinner';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import * as React from 'react';
// import { closeIcon } from "@jupyterlab/ui-components"; // Not available on JLab 1.x #8
const closeIcon = (React.createElement("svg", { "element-position": "center", height: "16px", viewBox: "0 0 24 24", width: "16px", xmlns: "http://www.w3.org/2000/svg" },
React.createElement("g", { className: "jp-icon-none jp-icon-selectable-inverse jp-icon3-hover", fill: "none" },
React.createElement("circle", { cx: "12", cy: "12", r: "11" })),
React.createElement("g", { className: "jp-icon3 jp-icon-selectable jp-icon-accent2-hover", fill: "#616161" },
React.createElement("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" })),
React.createElement("g", { className: "jp-icon-none jp-icon-busy", fill: "none" },
React.createElement("circle", { cx: "12", cy: "12", r: "7" }))));
export var INotification;
(function (INotification) {
/** Create a button with customized callback in a toast */
const ToastButton = ({ button, closeToast, }) => {
const fullClassName = button.className === undefined
? 'jp-toast-button'
: 'jp-toast-button ' + button.className;
const clickHandler = () => {
closeToast();
button.callback();
};
return (React.createElement("button", { className: fullClassName, onClick: clickHandler }, button.label));
};
/**
* Helper function to construct the notification content
*
* @param message Message to print in the notification
* @param closeHandler Function closing the notification
* @param buttons Toast buttons
*/
function createContent(message, closeHandler, buttons, icon) {
const hasButtons = buttons && buttons.length > 0;
return (React.createElement(React.Fragment, null,
icon ? icon : null,
message,
hasButtons && (React.createElement("div", { className: "jp-toast-buttonBar" },
React.createElement("div", { className: "jp-toast-spacer" }),
buttons.map((button, idx) => {
return (React.createElement(ToastButton, { key: 'button-' + idx, button: button, closeToast: closeHandler }));
})))));
}
async function createToast(message, buttons, options) {
let _resolve;
const toast = await Private.toast();
const promise = new Promise((resolve) => {
_resolve = resolve;
});
const theOptions = Object.assign({}, options);
const toastId = toast(({ closeToast }) => createContent(message, closeToast, buttons, Private.type2Icon.get(theOptions.type || 'in-progress')), Object.assign(Object.assign({}, options), { className: `jp-toast-${theOptions.type || 'in-progress'}`, onOpen: () => _resolve(toastId) }));
return promise;
}
/**
* Helper function to show an error notification. Those
* notifications need an user action to close.
*
* @param message Message to be printed in the notification
* @param options Options for the error notification
* @returns ToastId
*/
INotification.error = async (message, options) => {
return createToast(message, options && options.buttons, {
type: 'error',
autoClose: (options && options.autoClose) || false,
});
};
/**
* Helper function to show a warning notification. Those
* notifications need an user action to close.
*
* @param message Message to be printed in the notification
* @param options Options for the warning notification
* @returns ToastId
*/
INotification.warning = async (message, options) => {
return createToast(message, options && options.buttons, {
type: 'warning',
autoClose: (options && options.autoClose) || false,
});
};
/**
* Helper function to show an informative notification. Those
* notifications close automatically.
*
* @param message Message to be printed in the notification
* @param options Options for the error notification
* @returns ToastId
*/
INotification.info = async (message, options) => {
const theOptions = Object.assign({}, options);
const buttons = theOptions.buttons;
const autoClose = theOptions.autoClose ||
(buttons && buttons.length > 0 ? false : undefined);
return createToast(message, buttons, {
type: 'info',
autoClose: autoClose,
});
};
/**
* Helper function to show a success notification. Those
* notifications close automatically.
*
* @param message Message to be printed in the notification
* @param options Options for the error notification
* @returns ToastId
*/
INotification.success = async (message, options) => {
const theOptions = Object.assign({}, options);
const buttons = theOptions.buttons;
const autoClose = theOptions.autoClose ||
(buttons && buttons.length > 0 ? false : undefined);
return createToast(message, buttons, {
type: 'success',
autoClose: autoClose,
});
};
/**
* Helper function to show a in progress notification. Those
* notifications do not close automatically.
*
* @param message Message to be printed in the notification
* @param options Options for the error notification
* @returns ToastId
*/
INotification.inProgress = async (message, options) => {
return createToast(message, options && options.buttons, {
autoClose: (options && options.autoClose) || false,
});
};
/**
* Update an existing toast.
*
* If the toast is inactive (i.e. closed), a new one with the provided id
* will be created with the new content.
*
* @param args Update options
*/
INotification.update = async (args) => {
const toast = await Private.toast();
const buttons = args.buttons;
const options = {};
if (args.type) {
options.type = args.type;
}
const autoClose = args.autoClose || (buttons && buttons.length > 0 ? false : undefined);
if (autoClose) {
options.autoClose = autoClose;
}
if (toast.isActive(args.toastId)) {
// Update existing toast
const closeToast = () => {
toast.dismiss(args.toastId);
};
toast.update(args.toastId, Object.assign(Object.assign({}, options), { render: createContent(args.message, closeToast, args.buttons,
// If not type specified, assumes it is `in progress`
Private.type2Icon.get(options.type || 'in-progress')) }));
}
else {
// Needs to recreate a closed toast
// If not type specified, assumes it is `in progress`
const newOptions = Object.assign({ autoClose: false, toastId: args.toastId }, options);
await createToast(args.message, args.buttons, newOptions);
}
};
/**
* Dismiss one toast (specified by its id) or all if no id provided
*
* @param toastId Toast id
* @returns False or void
*/
INotification.dismiss = async (toastId) => {
const toast = await Private.toast();
return toast.dismiss(toastId);
};
/**
* Proxy to `toast` function from `react-toastify` module
*
* The Promise is due to the asynchronous import of the dependency
*
* @param content Toast content
* @param options Toast creation option
* @returns ToastId
*/
INotification.notify = async (content, options) => {
const toast = await Private.toast();
return toast(content, options);
};
})(INotification || (INotification = {}));
var Private;
(function (Private) {
Private.type2Icon = new Map([
['default', null],
[
'in-progress',
React.createElement(FontAwesomeIcon, { icon: faSpinner, pull: "left", spin: true, style: { color: 'var(--jp-inverse-layout-color3)' } }),
],
[
'error',
React.createElement(FontAwesomeIcon, { icon: faExclamationCircle, pull: "left", style: { color: 'var(--jp-error-color1)' } }),
],
[
'warning',
React.createElement(FontAwesomeIcon, { icon: faExclamationTriangle, pull: "left", style: { color: 'var(--jp-warn-color1)' } }),
],
[
'info',
React.createElement(FontAwesomeIcon, { icon: faBell, pull: "left", style: { color: 'var(--jp-info-color1)' } }),
],
[
'success',
React.createElement(FontAwesomeIcon, { icon: faCheck, pull: "left", style: { color: 'var(--jp-success-color1)' } }),
],
]);
let toastify = null;
const CloseButton = ({ closeToast, }) => (React.createElement("i", { onClick: closeToast },
React.createElement("span", { className: "jp-icon-hover" }, closeIcon)));
async function toast() {
if (toastify === null) {
toastify = await import('react-toastify');
toastify.toast.configure({
draggable: false,
closeOnClick: false,
hideProgressBar: true,
newestOnTop: true,
pauseOnFocusLoss: true,
pauseOnHover: true,
position: 'bottom-right',
className: 'jp-toastContainer',
transition: toastify.Slide,
closeButton: CloseButton,
});
}
return toastify.toast;
}
Private.toast = toast;
})(Private || (Private = {}));