muinotify
Version:
muinotify has an imperative API that makes it easy to display snackbars, without having to handle their open/close state. It also enables you to stack them on top of one another (although this is discouraged by the Material Design guidelines).
145 lines (124 loc) • 3.72 kB
JavaScript
import React from "react";
import { createPortal } from "react-dom";
import Notifier from "./context";
import NotifyWrapper from "./notifyWrapper";
import { Slide } from "@mui/material";
// TODO
// 1 - custom Components --> Done
// 2 - exit animation --> Done
// 3 - prevent duplicate --> Done
// 4 - seprate persist unlimited persist
// 5 - use Queue from the begining all things go to queue and then extraced them
class NotifyerProvider extends React.PureComponent {
constructor(props) {
super(props);
const { wrapperElement, wrapperName } = this.props;
// make a place for puting notifys
const rezvaniNotifyWrapper = document.createElement(
wrapperElement || "div"
);
rezvaniNotifyWrapper.className = wrapperName || "rezvani-notify-wrapper";
document.body.appendChild(rezvaniNotifyWrapper);
this.renderPortal = rezvaniNotifyWrapper;
this.actionQueue = [];
this.isStateBlocked = false;
this.state = {
notifyList: [],
};
}
getList = () => {
return [...this.state.notifyList];
};
// seconed arg is for blocking or unblocking setState
// more description below
commitState = (notifyList, isBlocked) => {
this.isStateBlocked = isBlocked;
this.setState({ notifyList }, () => {
if (!isBlocked) this.proccessQueue();
});
};
proccessQueue = () => {
const action = this.actionQueue.shift();
if (action) action();
};
notifier = (message, options) => {
const { maxNotif, preventDuplicate } = this.props;
const ID = Math.random().toString(16).slice(2);
const newData = {
message,
options,
isEntered: true,
id: ID,
};
const list = this.getList();
// prevent duplicate
if (
list.findIndex((item) => item.message === message) >= 0 &&
preventDuplicate
)
return;
if (this.isStateBlocked) {
this.actionQueue.push(() => this.notifier(message, options));
return;
}
if (list.length >= maxNotif) {
list[0].isEntered = false;
list.push(newData);
this.commitState(list, true);
} else {
list.push(newData);
this.commitState(list, false);
}
return ID;
};
removeNofityById = (ID) => {
const notifyList = [...this.state.notifyList];
const newList = notifyList.filter((elem) => elem.id !== ID);
// seconed arg is just for telling the state that after removing notif from list
// i want to set new state but before that every thing goes to queue
this.commitState(newList, false);
};
removeNotifierAnimationById = (ID) => {
const list = this.getList();
const index = list.findIndex((item) => item.id === ID);
if (index >= 0) list[index].isEntered = false;
this.commitState(list, false);
};
render() {
const { notifyList } = this.state;
const { children, ...restProps } = this.props;
return (
<Notifier.Provider
value={{
notifier: this.notifier,
removeNotifier: this.removeNotifierAnimationById,
}}
>
{children}
{createPortal(
<NotifyWrapper
notifyList={notifyList}
remove={this.removeNofityById}
{...restProps}
/>,
this.renderPortal
)}
</Notifier.Provider>
);
}
}
NotifyerProvider.defaultProps = {
customAlert: null,
preventDuplicate: false,
position: "bottom-left",
wrapperElement: "div",
wrapperName: "rezvani-notify-wrapper",
maxNotif: 3,
defaultSeverity: "info",
generalTimeOut: 3000,
TransitionComponent: Slide,
TransitionProps: {},
AlertProps: {},
StackProps: { spacing: 1, direction: "column-reverse" },
};
export default NotifyerProvider;