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,115 +3,170 @@ import IconButton from "@material-ui/core/IconButton";
|
||||||
import Snackbar from "@material-ui/core/Snackbar";
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import CloseIcon from "@material-ui/icons/Close";
|
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 { IMessage, MessageContext } from "./";
|
||||||
|
import { useStyles } from "./styles";
|
||||||
|
|
||||||
interface Message extends IMessage {
|
interface Message extends IMessage {
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
interface MessageManagerState {
|
|
||||||
message: Message;
|
|
||||||
opened: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MessageManager extends React.Component<{}, MessageManagerState> {
|
export const MessageManager = props => {
|
||||||
state: MessageManagerState = {
|
const { children } = props;
|
||||||
message: { key: "0", onUndo: undefined, text: "" },
|
|
||||||
opened: false
|
|
||||||
};
|
|
||||||
queue = [];
|
|
||||||
|
|
||||||
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") {
|
if (reason === "clickaway") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ opened: false });
|
setOpened(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleExited = () => {
|
const processQueue = () => {
|
||||||
this.processQueue();
|
if (queue.length > 0) {
|
||||||
|
setMessage(queue.shift());
|
||||||
|
setOpened(true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pushMessage = (message: IMessage) => {
|
const handleExited = () => {
|
||||||
this.queue.push({
|
processQueue();
|
||||||
|
};
|
||||||
|
|
||||||
|
const pushMessage = (message: IMessage) => {
|
||||||
|
queue.push({
|
||||||
key: new Date().getTime(),
|
key: new Date().getTime(),
|
||||||
...message
|
...message
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.state.opened) {
|
if (opened) {
|
||||||
this.setState({ opened: false });
|
setOpened(false);
|
||||||
} else {
|
} else {
|
||||||
this.processQueue();
|
processQueue();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
processQueue = () => {
|
return (
|
||||||
if (this.queue.length > 0) {
|
<>
|
||||||
this.setState({
|
<Snackbar
|
||||||
message: this.queue.shift(),
|
key={key}
|
||||||
opened: true
|
anchorOrigin={{
|
||||||
});
|
horizontal: "right",
|
||||||
}
|
vertical: "top"
|
||||||
};
|
}}
|
||||||
|
open={opened}
|
||||||
render() {
|
autoHideDuration={autohide}
|
||||||
const { autohide = 3000, title, text, key, onUndo } = this.state.message;
|
onClose={handleClose}
|
||||||
return (
|
onExited={handleExited}
|
||||||
<>
|
ContentProps={{
|
||||||
<Snackbar
|
"aria-describedby": "message-id"
|
||||||
key={key}
|
}}
|
||||||
anchorOrigin={{
|
className={classNames(classes.snackbar, {
|
||||||
horizontal: "right",
|
[classes.error]: status === "error",
|
||||||
vertical: "top"
|
[classes.success]: status === "success",
|
||||||
}}
|
[classes.warning]: status === "warning"
|
||||||
open={this.state.opened}
|
})}
|
||||||
autoHideDuration={autohide}
|
message={
|
||||||
onClose={this.handleClose}
|
<span id="message-id" data-tc="notification">
|
||||||
onExited={this.handleExited}
|
{title && (
|
||||||
ContentProps={{
|
<Typography variant="h5" style={{ fontWeight: "bold" }}>
|
||||||
"aria-describedby": "message-id"
|
{title}
|
||||||
}}
|
</Typography>
|
||||||
message={
|
)}
|
||||||
<span id="message-id" data-test="notification">
|
<Typography
|
||||||
{title && (
|
className={status === "info" ? classes.textInfo : classes.text}
|
||||||
<Typography variant="h5" style={{ marginBottom: "1rem" }}>
|
|
||||||
{title}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
{text}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
title={title}
|
|
||||||
action={[
|
|
||||||
!!onUndo ? (
|
|
||||||
<Button
|
|
||||||
key="undo"
|
|
||||||
color="secondary"
|
|
||||||
size="small"
|
|
||||||
onClick={this.handleClose as any}
|
|
||||||
data-test="button-undo"
|
|
||||||
>
|
|
||||||
UNDO
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
undefined
|
|
||||||
),
|
|
||||||
<IconButton
|
|
||||||
key="close"
|
|
||||||
aria-label="Close"
|
|
||||||
color="inherit"
|
|
||||||
onClick={this.handleClose as any}
|
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
{text}
|
||||||
</IconButton>
|
</Typography>
|
||||||
]}
|
</span>
|
||||||
/>
|
}
|
||||||
<MessageContext.Provider value={this.pushMessage}>
|
title={title}
|
||||||
{this.props.children}
|
action={[
|
||||||
</MessageContext.Provider>
|
!!onUndo ? (
|
||||||
</>
|
<Button
|
||||||
);
|
key="undo"
|
||||||
}
|
color="default"
|
||||||
}
|
size="small"
|
||||||
|
onClick={handleClose as any}
|
||||||
|
data-tc="button-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
|
||||||
|
),
|
||||||
|
<IconButton
|
||||||
|
key="close"
|
||||||
|
aria-label="Close"
|
||||||
|
color="inherit"
|
||||||
|
onClick={handleClose as any}
|
||||||
|
className={classes.closeBtn}
|
||||||
|
>
|
||||||
|
<CloseIcon />
|
||||||
|
</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={pushMessage}>
|
||||||
|
{children}
|
||||||
|
</MessageContext.Provider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default MessageManager;
|
export default MessageManager;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
|
|
||||||
export interface IMessage {
|
export interface IMessage {
|
||||||
|
action?: () => void;
|
||||||
autohide?: number;
|
autohide?: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
text: string;
|
text: string;
|
||||||
onUndo?: () => void;
|
onUndo?: () => void;
|
||||||
|
status?: "success" | "error" | "info" | "warning";
|
||||||
}
|
}
|
||||||
export type IMessageContext = (message: IMessage) => void;
|
export type IMessageContext = (message: IMessage) => void;
|
||||||
export const MessageContext = createContext<IMessageContext>(undefined);
|
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
|
color: colors.font.default
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
alignSelf: "baseline"
|
display: "block",
|
||||||
|
paddingBottom: 15,
|
||||||
|
paddingLeft: 0
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
fontSize: 16
|
fontSize: 16
|
||||||
|
@ -353,8 +355,7 @@ export default (colors: IThemeColors): Theme =>
|
||||||
boxShadow:
|
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)",
|
"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,
|
color: colors.font.default,
|
||||||
display: "grid",
|
display: "block",
|
||||||
gridTemplateColumns: "1fr 56px",
|
|
||||||
maxWidth: 480
|
maxWidth: 480
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue