update MessageManager
This commit is contained in:
parent
f4d570c7e5
commit
b3eb13a785
8 changed files with 279 additions and 90 deletions
5
assets/images/error-icon.svg
Normal file
5
assets/images/error-icon.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="16" fill="#F5FAFB"/>
|
||||
<rect x="10.2726" y="9.00024" width="17.9987" height="1.79987" transform="rotate(45 10.2726 9.00024)" fill="#FE6D76"/>
|
||||
<rect x="23" y="10.2727" width="17.9987" height="1.79987" transform="rotate(135 23 10.2727)" fill="#FE6D76"/>
|
||||
</svg>
|
After Width: | Height: | Size: 380 B |
3
assets/images/info-icon.svg
Normal file
3
assets/images/info-icon.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32ZM15 20V8H17V20H15ZM15 24V22H17V24H15Z" fill="#CAD8DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 321 B |
4
assets/images/success-icon.svg
Normal file
4
assets/images/success-icon.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="16" fill="#F5FAFB"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.9167 10.306L12.5417 23L7 16.0699L8.48477 14.8326L12.611 20.0641L22.4919 9L23.9167 10.306Z" fill="#60DAA0"/>
|
||||
</svg>
|
After Width: | Height: | Size: 312 B |
5
assets/images/warning-icon.svg
Normal file
5
assets/images/warning-icon.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="16" fill="#F5FAFB"/>
|
||||
<rect x="10.2726" y="9.00024" width="17.9987" height="1.79987" transform="rotate(45 10.2726 9.00024)" fill="#D98E1D"/>
|
||||
<rect x="23" y="10.2727" width="17.9987" height="1.79987" transform="rotate(135 23 10.2727)" fill="#D98E1D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 380 B |
|
@ -3,60 +3,72 @@ import IconButton from "@material-ui/core/IconButton";
|
|||
import Snackbar from "@material-ui/core/Snackbar";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import CloseIcon from "@material-ui/icons/Close";
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import React, { useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { IMessage, MessageContext } from "./";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
interface Message extends IMessage {
|
||||
key: string;
|
||||
}
|
||||
interface MessageManagerState {
|
||||
message: Message;
|
||||
opened: boolean;
|
||||
}
|
||||
|
||||
export class MessageManager extends React.Component<{}, MessageManagerState> {
|
||||
state: MessageManagerState = {
|
||||
message: { key: "0", onUndo: undefined, text: "" },
|
||||
opened: false
|
||||
};
|
||||
queue = [];
|
||||
export const MessageManager = props => {
|
||||
const { children } = props;
|
||||
|
||||
handleClose = (_, reason) => {
|
||||
const [message, setMessage] = useState<Message>({
|
||||
key: "0",
|
||||
onUndo: undefined,
|
||||
status: "info",
|
||||
text: ""
|
||||
});
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
const classes = useStyles({});
|
||||
const {
|
||||
action,
|
||||
autohide = 3000,
|
||||
title,
|
||||
text,
|
||||
key,
|
||||
onUndo,
|
||||
status = "info"
|
||||
} = message;
|
||||
|
||||
const queue = [];
|
||||
|
||||
const handleClose = (_, reason) => {
|
||||
if (reason === "clickaway") {
|
||||
return;
|
||||
}
|
||||
this.setState({ opened: false });
|
||||
setOpened(false);
|
||||
};
|
||||
|
||||
handleExited = () => {
|
||||
this.processQueue();
|
||||
const processQueue = () => {
|
||||
if (queue.length > 0) {
|
||||
setMessage(queue.shift());
|
||||
setOpened(true);
|
||||
}
|
||||
};
|
||||
|
||||
pushMessage = (message: IMessage) => {
|
||||
this.queue.push({
|
||||
const handleExited = () => {
|
||||
processQueue();
|
||||
};
|
||||
|
||||
const pushMessage = (message: IMessage) => {
|
||||
queue.push({
|
||||
key: new Date().getTime(),
|
||||
...message
|
||||
});
|
||||
|
||||
if (this.state.opened) {
|
||||
this.setState({ opened: false });
|
||||
if (opened) {
|
||||
setOpened(false);
|
||||
} else {
|
||||
this.processQueue();
|
||||
processQueue();
|
||||
}
|
||||
};
|
||||
|
||||
processQueue = () => {
|
||||
if (this.queue.length > 0) {
|
||||
this.setState({
|
||||
message: this.queue.shift(),
|
||||
opened: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { autohide = 3000, title, text, key, onUndo } = this.state.message;
|
||||
return (
|
||||
<>
|
||||
<Snackbar
|
||||
|
@ -65,21 +77,30 @@ export class MessageManager extends React.Component<{}, MessageManagerState> {
|
|||
horizontal: "right",
|
||||
vertical: "top"
|
||||
}}
|
||||
open={this.state.opened}
|
||||
open={opened}
|
||||
autoHideDuration={autohide}
|
||||
onClose={this.handleClose}
|
||||
onExited={this.handleExited}
|
||||
onClose={handleClose}
|
||||
onExited={handleExited}
|
||||
ContentProps={{
|
||||
"aria-describedby": "message-id"
|
||||
}}
|
||||
className={classNames(classes.snackbar, {
|
||||
[classes.error]: status === "error",
|
||||
[classes.success]: status === "success",
|
||||
[classes.warning]: status === "warning"
|
||||
})}
|
||||
message={
|
||||
<span id="message-id" data-test="notification">
|
||||
<span id="message-id" data-tc="notification">
|
||||
{title && (
|
||||
<Typography variant="h5" style={{ marginBottom: "1rem" }}>
|
||||
<Typography variant="h5" style={{ fontWeight: "bold" }}>
|
||||
{title}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography
|
||||
className={status === "info" ? classes.textInfo : classes.text}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
</span>
|
||||
}
|
||||
title={title}
|
||||
|
@ -87,12 +108,34 @@ export class MessageManager extends React.Component<{}, MessageManagerState> {
|
|||
!!onUndo ? (
|
||||
<Button
|
||||
key="undo"
|
||||
color="secondary"
|
||||
color="default"
|
||||
size="small"
|
||||
onClick={this.handleClose as any}
|
||||
data-test="button-undo"
|
||||
onClick={handleClose as any}
|
||||
data-tc="button-undo"
|
||||
>
|
||||
UNDO
|
||||
<FormattedMessage
|
||||
defaultMessage="Undo"
|
||||
description="snackbar button undo"
|
||||
/>
|
||||
</Button>
|
||||
) : (
|
||||
undefined
|
||||
),
|
||||
!!action ? (
|
||||
<Button
|
||||
key="action"
|
||||
color="default"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
action();
|
||||
handleClose(null, null);
|
||||
}}
|
||||
data-tc="button-action"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Action"
|
||||
description="snackbar button action"
|
||||
/>
|
||||
</Button>
|
||||
) : (
|
||||
undefined
|
||||
|
@ -101,17 +144,29 @@ export class MessageManager extends React.Component<{}, MessageManagerState> {
|
|||
key="close"
|
||||
aria-label="Close"
|
||||
color="inherit"
|
||||
onClick={this.handleClose as any}
|
||||
onClick={handleClose as any}
|
||||
className={classes.closeBtn}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</IconButton>,
|
||||
<div className={classes.progressBarContainer} key="progressBar">
|
||||
<div
|
||||
className={classNames(classes.progressBar, {
|
||||
[classes.progressBarActive]: opened,
|
||||
[classes.progressBarSuccess]: status === "success",
|
||||
[classes.progressBarWarning]: status === "warning",
|
||||
[classes.progressBarError]: status === "error"
|
||||
})}
|
||||
style={{ ["--animationTime" as any]: `${autohide}ms` }}
|
||||
/>
|
||||
</div>
|
||||
]}
|
||||
/>
|
||||
<MessageContext.Provider value={this.pushMessage}>
|
||||
{this.props.children}
|
||||
<MessageContext.Provider value={pushMessage}>
|
||||
{children}
|
||||
</MessageContext.Provider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default MessageManager;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { createContext } from "react";
|
||||
|
||||
export interface IMessage {
|
||||
action?: () => void;
|
||||
autohide?: number;
|
||||
title?: string;
|
||||
text: string;
|
||||
onUndo?: () => void;
|
||||
status?: "success" | "error" | "info" | "warning";
|
||||
}
|
||||
export type IMessageContext = (message: IMessage) => void;
|
||||
export const MessageContext = createContext<IMessageContext>(undefined);
|
||||
|
|
114
src/components/messages/styles.ts
Normal file
114
src/components/messages/styles.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
import errorIcon from "@assets/images/error-icon.svg";
|
||||
import infoIcon from "@assets/images/info-icon.svg";
|
||||
import successIcon from "@assets/images/success-icon.svg";
|
||||
import warningIcon from "@assets/images/warning-icon.svg";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { darken } from "@material-ui/core/styles/colorManipulator";
|
||||
|
||||
const successColor = "#60DAA0";
|
||||
const warningColor = "#FFB84E";
|
||||
const infoColor = "#CAD8DF";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
"@keyframes bar": {
|
||||
from: { transform: "translateX(-100%)" },
|
||||
to: { transform: "translateX(0)" }
|
||||
},
|
||||
closeBtn: {
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
error: {
|
||||
"& > div": {
|
||||
"& button span": {
|
||||
color: "#fff"
|
||||
},
|
||||
"&:before": {
|
||||
backgroundImage: `url(${errorIcon})`
|
||||
},
|
||||
|
||||
backgroundColor: theme.palette.error.main,
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
progressBar: {
|
||||
backgroundColor: infoColor,
|
||||
height: 8,
|
||||
transform: "translateX(-100%)",
|
||||
width: "100%"
|
||||
},
|
||||
progressBarActive: {
|
||||
animation: `$bar var(--animationTime) ease both`
|
||||
},
|
||||
progressBarContainer: {
|
||||
borderRadius: "0 0 4px 4px",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
overflow: "hidden",
|
||||
position: "absolute",
|
||||
width: "calc(100%)"
|
||||
},
|
||||
progressBarError: {
|
||||
backgroundColor: darken(theme.palette.error.main, 0.2)
|
||||
},
|
||||
progressBarSuccess: {
|
||||
backgroundColor: darken(successColor, 0.2)
|
||||
},
|
||||
progressBarWarning: {
|
||||
backgroundColor: darken(warningColor, 0.2)
|
||||
},
|
||||
snackbar: {
|
||||
"& > div": {
|
||||
"&:before": {
|
||||
backgroundImage: `url(${infoIcon})`,
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "contain",
|
||||
content: "''",
|
||||
display: "block",
|
||||
height: 32,
|
||||
left: 15,
|
||||
position: "absolute",
|
||||
top: 13,
|
||||
width: 32
|
||||
},
|
||||
paddingLeft: 60,
|
||||
position: "relative"
|
||||
}
|
||||
},
|
||||
success: {
|
||||
"& > div": {
|
||||
"& button span": {
|
||||
color: "#fff"
|
||||
},
|
||||
"&:before": {
|
||||
backgroundImage: `url(${successIcon})`
|
||||
},
|
||||
|
||||
backgroundColor: successColor,
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
text: {
|
||||
color: "#fff",
|
||||
paddingTop: 5
|
||||
},
|
||||
textInfo: {
|
||||
paddingTop: 5
|
||||
},
|
||||
warning: {
|
||||
"& > div": {
|
||||
"& button span": {
|
||||
color: "#fff"
|
||||
},
|
||||
"&:before": {
|
||||
backgroundImage: `url(${warningIcon})`
|
||||
},
|
||||
backgroundColor: warningColor,
|
||||
color: "#fff"
|
||||
}
|
||||
}
|
||||
}),
|
||||
{ name: "MessageManager" }
|
||||
);
|
|
@ -343,7 +343,9 @@ export default (colors: IThemeColors): Theme =>
|
|||
color: colors.font.default
|
||||
}
|
||||
},
|
||||
alignSelf: "baseline"
|
||||
display: "block",
|
||||
paddingBottom: 15,
|
||||
paddingLeft: 0
|
||||
},
|
||||
message: {
|
||||
fontSize: 16
|
||||
|
@ -353,8 +355,7 @@ export default (colors: IThemeColors): Theme =>
|
|||
boxShadow:
|
||||
"0 6px 10px 0px rgba(0, 0, 0, 0.15), 0 1px 18px 0px rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.10)",
|
||||
color: colors.font.default,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 56px",
|
||||
display: "block",
|
||||
maxWidth: 480
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue