import { useState, ReactNode, ComponentProps } from "react"; import classNames from "classnames"; import { useC, Text, ThemeColor } from "../common"; import { useInputs, InputGroup, Initializer as InputInitializer, InputValueDict, InputErrorDict, } from "../input/InputGroup"; import Dialog from "./Dialog"; import DialogContainer from "./DialogContainer"; import "./OperationDialog.css"; export type { InputInitializer, InputValueDict, InputErrorDict }; interface OperationDialogPromptProps { message?: Text; customMessage?: Text; customMessageNode?: ReactNode; className?: string; } function OperationDialogPrompt(props: OperationDialogPromptProps) { const { message, customMessage, customMessageNode, className } = props; const c = useC(); return (
{message &&

{c(message)}

} {customMessageNode ?? (customMessage != null ? c(customMessage) : null)}
); } export interface OperationDialogProps { open: boolean; onClose: () => void; color?: ThemeColor; inputColor?: ThemeColor; title: Text; inputPrompt?: Text; inputPromptNode?: ReactNode; successPrompt?: (data: TData) => Text; successPromptNode?: (data: TData) => ReactNode; failurePrompt?: (error: unknown) => Text; failurePromptNode?: (error: unknown) => ReactNode; inputs: InputInitializer; onProcess: (inputs: InputValueDict) => Promise; onSuccessAndClose?: (data: TData) => void; } function OperationDialog(props: OperationDialogProps) { const { open, onClose, color, inputColor, title, inputPrompt, inputPromptNode, successPrompt, successPromptNode, failurePrompt, failurePromptNode, inputs, onProcess, onSuccessAndClose, } = props; if (process.env.NODE_ENV === "development") { if (inputPrompt && inputPromptNode) { console.log("InputPrompt and inputPromptNode are both set."); } if (successPrompt && successPromptNode) { console.log("SuccessPrompt and successPromptNode are both set."); } if (failurePrompt && failurePromptNode) { console.log("FailurePrompt and failurePromptNode are both set."); } } type Step = | { type: "input" } | { type: "process" } | { type: "success"; data: TData; } | { type: "failure"; data: unknown; }; const [step, setStep] = useState({ type: "input" }); const { inputGroupProps, hasErrorAndDirty, setAllDisabled, confirm } = useInputs({ init: inputs, }); function close() { if (step.type !== "process") { onClose(); if (step.type === "success" && onSuccessAndClose) { onSuccessAndClose?.(step.data); } } else { console.log("Attempt to close modal dialog when processing."); } } function onConfirm() { const result = confirm(); if (result.type === "ok") { setStep({ type: "process" }); setAllDisabled(true); onProcess(result.values).then( (d) => { setStep({ type: "success", data: d, }); }, (e: unknown) => { setStep({ type: "failure", data: e, }); }, ); } } let body: ReactNode; let buttons: ComponentProps["buttons"]; if (step.type === "input" || step.type === "process") { const isProcessing = step.type === "process"; body = (
); buttons = [ { key: "cancel", type: "normal", props: { text: "operationDialog.cancel", color: "secondary", outline: true, onClick: close, disabled: isProcessing, }, }, { key: "confirm", type: "loading", props: { text: "operationDialog.confirm", color, loading: isProcessing, disabled: hasErrorAndDirty, onClick: onConfirm, }, }, ]; } else { const result = step; const promptProps: OperationDialogPromptProps = result.type === "success" ? { message: "operationDialog.success", customMessage: successPrompt?.(result.data), customMessageNode: successPromptNode?.(result.data), } : { message: "operationDialog.error", customMessage: failurePrompt?.(result.data), customMessageNode: failurePromptNode?.(result.data), }; body = (
); buttons = [ { key: "ok", type: "normal", props: { text: "operationDialog.ok", color: "primary", onClick: close, }, }, ]; } return ( {body} ); } export default OperationDialog;