@etsoo/materialui
Version:
TypeScript Material-UI Implementation
384 lines (383 loc) • 19 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { NotificationAlign, NotificationMessageType, NotificationType } from "@etsoo/notificationbase";
import { DomUtils } from "@etsoo/shared";
import React from "react";
import { NotificationReact, NotifierReact } from "@etsoo/react";
import { DraggablePaperComponent } from "./DraggablePaperComponent";
import { LoadingButton } from "./LoadingButton";
import { Labels } from "./app/Labels";
import DialogTitle from "@mui/material/DialogTitle";
import { styled } from "@mui/material/styles";
import Dialog from "@mui/material/Dialog";
import IconButton from "@mui/material/IconButton";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import DialogContentText from "@mui/material/DialogContentText";
import Fade from "@mui/material/Fade";
import Alert from "@mui/material/Alert";
import AlertTitle from "@mui/material/AlertTitle";
import Switch from "@mui/material/Switch";
import Slider from "@mui/material/Slider";
import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button";
import Popover from "@mui/material/Popover";
import CircularProgress from "@mui/material/CircularProgress";
import Backdrop from "@mui/material/Backdrop";
import Box from "@mui/material/Box";
import Snackbar from "@mui/material/Snackbar";
import CloseIcon from "@mui/icons-material/Close";
import DoneIcon from "@mui/icons-material/Done";
import InfoIcon from "@mui/icons-material/Info";
import WarningIcon from "@mui/icons-material/Warning";
import ErrorIcon from "@mui/icons-material/Error";
import HelpIcon from "@mui/icons-material/Help";
// Custom icon dialog title bar
const IconDialogTitle = styled(DialogTitle, {
// Prevent the passing of the draggable prop to the underlying DOM element
shouldForwardProp: (prop) => prop !== "draggable"
}) `
${({ theme, draggable }) => `
cursor: ${draggable ? "move" : "default"};
display: flex;
align-items: center;
& .dialogTitle {
font-weight: bold;
font-size: 1.17em;
flex-grow: 3;
text-overflow: ellipsis;
padding-left: ${theme.spacing(1)};
}
& .MuiDialogContent-root-close-button {
padding-left: ${theme.spacing(1)};
}
`}
`;
/**
* MU notification
*/
export class NotificationMU extends NotificationReact {
// Create alert
createAlert(_props, className) {
const labels = Labels.NotificationMU;
const { buttons, inputs, fullScreen, fullWidth = true, maxWidth, okLabel = labels.alertOK, primaryButton = true, primaryButtonProps, closable = false, draggable = fullScreen === true ? false : true } = this.inputProps ?? {};
let title = this.title;
let icon;
if (this.type === NotificationMessageType.Success) {
icon = _jsx(DoneIcon, { color: "primary" });
title ??= labels.success;
}
else if (this.type === NotificationMessageType.Info) {
icon = _jsx(InfoIcon, {});
title ??= labels.info;
}
else if (this.type === NotificationMessageType.Warning) {
icon = _jsx(WarningIcon, { color: "secondary" });
title ??= labels.warning;
}
else {
icon = _jsx(ErrorIcon, { color: "error" });
title ??= labels.alertTitle;
}
const setupProps = {
color: "primary"
};
// Setup callback
const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
// Callback
const callback = async (_event) => {
await this.returnValue(undefined);
return true;
};
return (_jsxs(Dialog, { open: this.open, PaperComponent: draggable ? DraggablePaperComponent : undefined, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options, children: [_jsxs(IconDialogTitle, { draggable: draggable, className: draggable ? "dialog-title draggable-dialog-title" : "dialog-title", children: [icon, _jsx("span", { className: "dialogTitle", children: title }), closable && (_jsx(IconButton, { className: "MuiDialogContent-root-close-button", size: "small", onClick: () => this.returnValue("CLOSE"), children: _jsx(CloseIcon, {}) }))] }), _jsxs(DialogContent, { children: [typeof this.content === "string" ? (_jsx(DialogContentText, { children: this.content })) : (this.content), inputs] }), _jsx(DialogActions, { children: buttons
? buttons(this, callback)
: primaryButton && (_jsx(LoadingButton, { ...setupProps, onClick: callback, autoFocus: true, ...primaryButtonProps, children: okLabel })) })] }, this.id));
}
// Create confirm
createConfirm(_props, className) {
const labels = Labels.NotificationMU;
const title = this.title ?? labels.confirmTitle;
const { buttons, okLabel = labels.confirmYes, cancelLabel = labels.confirmNo, cancelButton = true, inputs, fullScreen, fullWidth = true, maxWidth, primaryButton = true, primaryButtonProps, closable = false, draggable = fullScreen === true ? false : true } = this.inputProps ?? {};
// Setup callback
const options = this.renderSetup ? this.renderSetup({}) : undefined;
const callback = async (_event, value) => {
await this.returnValue(value);
return true;
};
return (_jsxs(Dialog, { open: this.open, PaperComponent: draggable ? DraggablePaperComponent : undefined, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options, children: [_jsxs(IconDialogTitle, { draggable: draggable, className: draggable ? "dialog-title draggable-dialog-title" : "dialog-title", children: [_jsx(HelpIcon, { color: "action" }), _jsx("span", { className: "dialogTitle", children: title }), closable && (_jsx(IconButton, { className: "MuiDialogContent-root-close-button", size: "small", onClick: () => this.returnValue("CLOSE"), children: _jsx(CloseIcon, {}) }))] }), _jsxs(DialogContent, { children: [typeof this.content === "string" ? (_jsx(DialogContentText, { children: this.content })) : (this.content), inputs] }), _jsx(DialogActions, { children: buttons ? (buttons(this, callback)) : (_jsxs(React.Fragment, { children: [cancelButton && (_jsx(LoadingButton, { color: "secondary", onClick: async (event) => await callback(event, false), children: cancelLabel })), primaryButton && (_jsx(LoadingButton, { color: "primary", onClick: async (event) => await callback(event, true), autoFocus: true, ...primaryButtonProps, children: okLabel }))] })) })] }, this.id));
}
createMessageColor() {
if (this.type === NotificationMessageType.Danger)
return "error";
if (this.type === NotificationMessageType.Success)
return "success";
if (this.type === NotificationMessageType.Warning)
return "warning";
return "info";
}
// Create message
createMessage(props, className) {
if (!this.open)
return _jsx(React.Fragment, {}, this.id);
const { closable = false } = props;
const setupProps = {
severity: this.createMessageColor(),
variant: "filled"
};
// Setup callback
const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
return (_jsx(Fade, { in: true, children: _jsxs(Alert, { ...setupProps, ...options, action: closable ? (_jsx(IconButton, { size: "small", onClick: () => this.returnValue("CLOSE"), children: _jsx(CloseIcon, {}) })) : undefined, onClose: () => this.dismiss(), className: className, children: [this.title && _jsx(AlertTitle, { children: this.title }), this.content] }) }, this.id));
}
// Create prompt
createPrompt(_props, className) {
const labels = Labels.NotificationMU;
const title = this.title ?? labels.promptTitle;
const { buttons, cancelLabel = labels.promptCancel, okLabel = labels.promptOK, cancelButton = true, inputs, type, fullScreen, fullWidth = true, maxWidth, primaryButton = true, primaryButtonProps, inputProps, closable = false, draggable = fullScreen === true ? false : true } = this.inputProps ?? {};
const inputRef = React.createRef();
const errorRef = React.createRef();
const setError = (error) => {
if (errorRef.current == null)
return;
errorRef.current.innerText = error ?? "";
};
const handleSubmit = async (event) => {
// Result
let result = undefined;
const input = inputRef.current;
if (this.onReturn) {
// Inputs case, no HTMLForm set to value, set the current form
if (inputs && value == null)
value = event.currentTarget.form;
if (input) {
if (type === "switch") {
const boolValue = input.value === "true";
result = this.onReturn(boolValue);
}
else {
const inputValue = DomUtils.getInputValue(input);
if ((inputValue == null || inputValue === "") && input.required) {
input.focus();
return false;
}
result = this.onReturn(inputValue);
}
}
else if (value != null) {
result = this.onReturn(value);
}
}
// Get the value
// returns false to prevent default dismiss
const v = await result;
if (v === false) {
input?.focus();
return false;
}
if (typeof v === "string") {
setError(v);
input?.focus();
return false;
}
this.dismiss();
return true;
};
let localInputs;
let value = undefined;
if (inputs == null) {
if (type === "switch") {
localInputs = (_jsx(Switch, { inputRef: inputRef, ...inputProps, value: "true", autoFocus: true, required: true }));
}
else if (type === "slider") {
localInputs = _jsx(Slider, { onChange: (_e, v) => (value = v) });
}
else {
localInputs = (_jsx(TextField, { inputRef: inputRef, onChange: () => setError(undefined), autoFocus: true, margin: "dense", fullWidth: true, type: type, required: true, ...inputProps }));
}
}
else {
localInputs = inputs;
}
// Setup callback
const options = this.renderSetup ? this.renderSetup({}) : undefined;
return (_jsx(Dialog, { open: this.open, PaperComponent: draggable ? DraggablePaperComponent : undefined, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options, children: _jsxs("form", { onSubmit: (event) => {
event.preventDefault();
event.currentTarget.elements.namedItem("okButton")?.click();
return false;
}, children: [_jsxs(IconDialogTitle, { draggable: draggable, className: draggable ? "dialog-title draggable-dialog-title" : "dialog-title", children: [_jsx(InfoIcon, { color: "primary" }), _jsx("span", { className: "dialogTitle", children: title }), closable && (_jsx(IconButton, { className: "MuiDialogContent-root-close-button", size: "small", onClick: () => this.returnValue("CLOSE"), children: _jsx(CloseIcon, {}) }))] }), _jsxs(DialogContent, { children: [typeof this.content === "string" ? (_jsx(DialogContentText, { children: this.content })) : (this.content), localInputs, _jsx(Typography, { variant: "caption", display: "block", ref: errorRef, color: "error" })] }), _jsx(DialogActions, { children: buttons ? (buttons(this, handleSubmit)) : (_jsxs(React.Fragment, { children: [cancelButton && (_jsx(Button, { color: "secondary", onClick: () => {
if (this.onReturn)
this.onReturn(undefined);
this.dismiss();
}, children: cancelLabel })), primaryButton && (_jsx(LoadingButton, { color: "primary", autoFocus: true, onClick: handleSubmit, name: "okButton", ...primaryButtonProps, children: okLabel }))] })) })] }) }, this.id));
}
createPopup(_props, className) {
// Destruct
// dismiss will trigger onReturn callback
const { content, id, open } = this;
// Setup callback
const options = this.renderSetup ? this.renderSetup({}) : undefined;
return (_jsx(Popover, { open: open, className: className, onClose: () => this.dismiss(), ...options, children: content }, id));
}
// Create loading
createLoading(_props, className) {
// Properties
const setupProps = { color: "primary" };
const labels = Labels.NotificationMU;
// Input props
const { maxWidth = "xs" } = this.inputProps ?? {};
// Content
let content = this.content;
if (content === "@")
content = labels.loading.toString();
// Setup callback
const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
return (_jsx(Backdrop, { className: className, sx: {
color: "#fff",
zIndex: (theme) => theme.zIndex.modal + 1
}, open: this.open, ...options, children: _jsxs(Box, { display: "flex", flexDirection: "column", flexWrap: "nowrap", alignItems: "center", sx: {
"& > :not(style) + :not(style)": {
marginTop: (theme) => theme.spacing(1)
}
}, children: [_jsx(CircularProgress, { ...setupProps }), content && (_jsx(Box, { maxWidth: maxWidth === false ? undefined : maxWidth, children: content }))] }) }, this.id));
}
/**
* Render method
* @param props Props
* @param className Style class name
* @param classes Style classes
*/
render(props, className) {
// Loading bar
if (this.type === NotificationType.Loading) {
return this.createLoading(props, className);
}
else if (this.type === NotificationType.Confirm) {
return this.createConfirm(props, className);
}
else if (this.type === NotificationType.Prompt) {
return this.createPrompt(props, className);
}
else if (this.type === NotificationType.Popup) {
return this.createPopup(props, className);
}
else if (this.type === NotificationType.Error ||
(this.modal && this.type in NotificationMessageType)) {
// Alert or modal message
return this.createAlert(props, className);
}
else {
return this.createMessage(props, className);
}
}
}
/**
* Antd notifier
*/
export class NotifierMU extends NotifierReact {
/**
* Create state and return provider
* @param className Style class name
* @param debug Debug mode
* @returns Provider
*/
static setup(className, debug = false) {
className ??= "notifier-mu";
// Create an instance
const instance = new NotifierMU();
instance.debug = debug;
const provider = instance.createProvider(className, debug);
NotifierReact.updateInstance(instance);
return provider;
}
// Calculate origin from align property
static getOrigin(align) {
if (align === NotificationAlign.TopLeft) {
return {
horizontal: "left",
vertical: "top"
};
}
if (align === NotificationAlign.TopCenter) {
return {
horizontal: "center",
vertical: "top"
};
}
if (align === NotificationAlign.TopRight) {
return {
horizontal: "right",
vertical: "top"
};
}
if (align === NotificationAlign.BottomLeft) {
return {
horizontal: "left",
vertical: "bottom"
};
}
if (align === NotificationAlign.BottomCenter) {
return {
horizontal: "center",
vertical: "bottom"
};
}
if (align === NotificationAlign.BottomRight) {
return {
horizontal: "right",
vertical: "bottom"
};
}
return {
horizontal: "center",
vertical: "top"
};
}
/**
* Create align container
* @param align Align
* @param children Children
* @param options Other options
*/
createContainer = (align, children) => {
// Each align group, class equal to something similar to 'align-topleft'
const alignText = NotificationAlign[align].toLowerCase();
let className = `align-${alignText}`;
if (children.length === 0) {
return _jsx("div", { className: className }, `empty-${alignText}`);
}
if (align === NotificationAlign.Unknown) {
// div container for style control
return (_jsx("div", { className: className, children: children }, `div-${alignText}`));
}
// Use SnackBar for layout
return (_jsx(Snackbar, { anchorOrigin: NotifierMU.getOrigin(align), className: className, sx: align === NotificationAlign.Center
? { position: "fixed", top: "50%!important" }
: undefined, open: true, children: _jsx(Box, { display: "flex", flexDirection: "column", flexWrap: "nowrap", sx: {
"& > :not(style) + :not(style)": {
marginTop: (theme) => theme.spacing(1)
}
}, children: children }, `box-${alignText}`) }, `layout-${alignText}`));
};
/**
* Add raw definition
* @param data Notification data definition
* @param modal Show as modal
*/
addRaw(data, modal) {
// Destruct
const { type, content, title, align, timespan = modal ? 0 : undefined, ...rest } = data;
if (this.debug) {
console.debug("NotificationMU.addRaw", data);
}
// Setup
const n = new NotificationMU(type, content, title, align, timespan);
// Assign other properties
Object.assign(n, rest);
// Is modal
if (modal != null)
n.modal = modal;
// Add to the collection
this.add(n);
// Return
return n;
}
}