UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

384 lines (383 loc) 19 kB
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; } }