comunicconsole/src/ui/widgets/DialogsProvider.tsx

340 lines
7.5 KiB
TypeScript

/**
* Application dialogs provider
*
* @author Pierre Hubert
*/
import {
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
Button,
TextField,
Snackbar,
IconButton,
} from "@mui/material";
import { Close } from "@mui/icons-material";
import React, { FormEvent } from "react";
let cache: ApplicationDialogsProvider;
export interface TextInputOptions {
title?: string;
message?: string;
initialValue?: string;
minLength?: number;
maxLength?: number;
label: string;
type?: string;
validateInput?: (value: string) => boolean;
}
interface AppDiagProvState {
// Alert dialog
alertMessage: string;
showAlert: boolean;
// Simple snackbar
snackBarMessage: string;
showSnackBar: boolean;
// Confirm dialog
confirmMessage: string;
showConfirm: boolean;
resolveConfirm?: (confirmed: boolean) => void;
// Text input dialog
showInputDialog: boolean;
inputValue: string;
inputOptions: TextInputOptions;
resolveInput?: (text: string) => void;
}
export class ApplicationDialogsProvider extends React.Component<
{},
AppDiagProvState
> {
private acceptConfirm: () => void;
private rejectConfirm: () => void;
private cancelInput: () => void;
private confirmInput: () => void;
constructor(props: any) {
super(props);
this.state = {
// Alert dialog
alertMessage: "",
showAlert: false,
// Simple snackbar
snackBarMessage: "",
showSnackBar: false,
// Confirm dialog
showConfirm: false,
confirmMessage: "",
resolveConfirm: undefined,
// Text input dialog
showInputDialog: false,
inputValue: "",
inputOptions: {
label: "",
},
resolveInput: undefined,
};
this.handleCloseAlert = this.handleCloseAlert.bind(this);
this.handleCloseSnackbar = this.handleCloseSnackbar.bind(this);
this.acceptConfirm = this.handleCloseConfirm.bind(this, true);
this.rejectConfirm = this.handleCloseConfirm.bind(this, false);
this.submitInput = this.submitInput.bind(this);
this.handleInputValueChanged = this.handleInputValueChanged.bind(this);
this.cancelInput = this.handleCloseInput.bind(this, true);
this.confirmInput = this.handleCloseInput.bind(this, false);
}
showAlert(message: string) {
this.setState({
showAlert: true,
alertMessage: message,
});
}
handleCloseAlert() {
this.setState({ showAlert: false });
}
showSnackbar(message: string) {
this.setState({ showSnackBar: true, snackBarMessage: message });
}
handleCloseSnackbar(
_event: React.SyntheticEvent | React.MouseEvent | Event,
reason?: string
) {
if (reason === "clickaway") {
return;
}
this.setState({ showSnackBar: false });
}
showConfirm(message: string): Promise<boolean> {
return new Promise((res, _rej) => {
this.setState({
showConfirm: true,
confirmMessage: message,
resolveConfirm: res,
});
});
}
handleCloseConfirm(accept: boolean) {
this.setState({
showConfirm: false,
});
this.state.resolveConfirm && this.state.resolveConfirm(accept);
}
showInput(options: TextInputOptions): Promise<string> {
return new Promise((res, _rej) => {
this.setState({
showInputDialog: true,
inputOptions: options,
resolveInput: res,
inputValue: options.initialValue || "",
});
});
}
handleInputValueChanged(e: React.ChangeEvent<HTMLInputElement>) {
this.setState({ inputValue: e.target.value });
}
submitInput(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
this.handleCloseInput(false);
}
handleCloseInput(cancel: boolean) {
if (!cancel && !this.isInputValid) return;
this.setState({
showInputDialog: false,
});
if (!cancel)
this.state.resolveInput &&
this.state.resolveInput(this.state.inputValue);
}
get isInputValid(): boolean {
const options = this.state.inputOptions;
const value = this.state.inputValue;
if (options.minLength && value.length < options.minLength) return false;
if (options.maxLength && value.length > options.maxLength) return false;
if (options.validateInput && !options.validateInput(value))
return false;
return true;
}
render() {
cache = this;
if (this.state == null) return <div></div>;
return (
<div>
{/* Simple alert dialog */}
<Dialog
open={this.state.showAlert}
onClose={this.handleCloseAlert}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title"
style={{ minWidth: "300px" }}
>
Message
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{this.state.alertMessage}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleCloseAlert} color="primary">
OK
</Button>
</DialogActions>
</Dialog>
{/* Simple snackbar */}
<Snackbar
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
open={this.state.showSnackBar}
autoHideDuration={6000}
onClose={this.handleCloseSnackbar}
message={this.state.snackBarMessage}
action={
<React.Fragment>
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={this.handleCloseSnackbar}
>
<Close fontSize="small" />
</IconButton>
</React.Fragment>
}
/>
{/* Confirm dialog */}
<Dialog
open={this.state.showConfirm}
onClose={this.rejectConfirm}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle
id="alert-dialog-title"
style={{ minWidth: "300px" }}
>
Confirm action
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{this.state.confirmMessage}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.rejectConfirm} color="secondary">
Cancel
</Button>
<Button onClick={this.acceptConfirm} color="secondary">
Confirm
</Button>
</DialogActions>
</Dialog>
{/* Text input dialog */}
<Dialog
open={this.state.showInputDialog}
onClose={this.rejectConfirm}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{this.state.inputOptions.title ||
this.state.inputOptions.label}
</DialogTitle>
<DialogContent>
{this.state.inputOptions.message ? (
<DialogContentText id="alert-dialog-description">
{this.state.inputOptions.message} <br />
<br />
</DialogContentText>
) : (
<span></span>
)}
<form onSubmit={this.submitInput}>
<TextField
label={this.state.inputOptions.label}
variant="outlined"
value={this.state.inputValue}
onChange={this.handleInputValueChanged}
type={this.state.inputOptions.type || "text"}
/>
</form>
</DialogContent>
<DialogActions>
<Button onClick={this.cancelInput} color="secondary">
Cancel
</Button>
<Button
onClick={this.confirmInput}
color="secondary"
disabled={!this.isInputValid}
>
OK
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
export function matAlert(msg: string) {
cache.showAlert(msg);
}
export function snackbar(msg: string) {
cache.showSnackbar(msg);
}
export function matConfirm(msg: string): Promise<boolean> {
return cache.showConfirm(msg);
}
export function input(options: TextInputOptions): Promise<string> {
return cache.showInput(options);
}