ente/web/packages/shared/components/SingleInputForm.tsx
2024-05-24 18:54:16 +05:30

207 lines
7.9 KiB
TypeScript

import { FlexWrapper } from "@ente/shared/components/Container";
import ShowHidePassword from "@ente/shared/components/Form/ShowHidePassword";
import { Button, FormHelperText } from "@mui/material";
import TextField from "@mui/material/TextField";
import { Formik, type FormikHelpers, type FormikState } from "formik";
import { t } from "i18next";
import React, { useMemo, useState } from "react";
import * as Yup from "yup";
import SubmitButton from "./SubmitButton";
interface formValues {
inputValue: string;
}
export interface SingleInputFormProps {
callback: (
inputValue: string,
setFieldError: (errorMessage: string) => void,
resetForm: (nextState?: Partial<FormikState<formValues>>) => void,
) => Promise<void>;
fieldType: "text" | "email" | "password";
/** deprecated: Use realPlaceholder */
placeholder?: string;
/**
* Placeholder
*
* The existing `placeholder` property uses the placeholder as a label (i.e.
* it doesn't appear as the placeholder within the text input area but
* rather as the label on top of it). This happens conditionally, so it is
* not a matter of simple rename.
*
* Gradually migrate the existing UI to use this property when we really
* want a placeholder, and then create a separate label property for places
* that actually want to set the label.
*/
realPlaceholder?: string;
/**
* Label to show on top of the text input area.
*
* Sibling of {@link realPlaceholder}.
*/
realLabel?: string;
buttonText: string;
submitButtonProps?: any;
initialValue?: string;
secondaryButtonAction?: () => void;
disableAutoFocus?: boolean;
hiddenPreInput?: any;
caption?: any;
hiddenPostInput?: any;
autoComplete?: string;
blockButton?: boolean;
hiddenLabel?: boolean;
disableAutoComplete?: boolean;
}
export default function SingleInputForm(props: SingleInputFormProps) {
const { submitButtonProps } = props;
const { sx: buttonSx, ...restSubmitButtonProps } = submitButtonProps ?? {};
const [loading, SetLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const submitForm = async (
values: formValues,
{ setFieldError, resetForm }: FormikHelpers<formValues>,
) => {
SetLoading(true);
await props.callback(
values.inputValue,
(message) => setFieldError("inputValue", message),
resetForm,
);
SetLoading(false);
};
const handleClickShowPassword = () => {
setShowPassword(!showPassword);
};
const handleMouseDownPassword = (
event: React.MouseEvent<HTMLButtonElement>,
) => {
event.preventDefault();
};
const validationSchema = useMemo(() => {
switch (props.fieldType) {
case "text":
return Yup.object().shape({
inputValue: Yup.string().required(t("REQUIRED")),
});
case "password":
return Yup.object().shape({
inputValue: Yup.string().required(t("REQUIRED")),
});
case "email":
return Yup.object().shape({
inputValue: Yup.string()
.email(t("EMAIL_ERROR"))
.required(t("REQUIRED")),
});
}
}, [props.fieldType]);
return (
<Formik<formValues>
initialValues={{ inputValue: props.initialValue ?? "" }}
onSubmit={submitForm}
validationSchema={validationSchema}
validateOnChange={false}
validateOnBlur={false}
>
{({ values, errors, handleChange, handleSubmit }) => (
<form noValidate onSubmit={handleSubmit}>
{props.hiddenPreInput}
<TextField
hiddenLabel={props.hiddenLabel}
variant="filled"
fullWidth
type={showPassword ? "text" : props.fieldType}
id={props.fieldType}
name={props.fieldType}
{...(props.hiddenLabel
? { placeholder: props.placeholder }
: props.realPlaceholder
? {
placeholder: props.realPlaceholder,
label: props.realLabel,
}
: { label: props.placeholder })}
value={values.inputValue}
onChange={handleChange("inputValue")}
error={Boolean(errors.inputValue)}
helperText={errors.inputValue}
disabled={loading}
autoFocus={!props.disableAutoFocus}
autoComplete={props.autoComplete}
InputProps={{
autoComplete:
props.disableAutoComplete ||
props.fieldType === "password"
? "off"
: "on",
endAdornment: props.fieldType === "password" && (
<ShowHidePassword
showPassword={showPassword}
handleClickShowPassword={
handleClickShowPassword
}
handleMouseDownPassword={
handleMouseDownPassword
}
/>
),
}}
/>
<FormHelperText
sx={{
position: "relative",
top: errors.inputValue ? "-22px" : "0",
float: "right",
padding: "0 8px",
}}
>
{props.caption}
</FormHelperText>
{props.hiddenPostInput}
<FlexWrapper
justifyContent={"flex-end"}
flexWrap={props.blockButton ? "wrap-reverse" : "nowrap"}
>
{props.secondaryButtonAction && (
<Button
onClick={props.secondaryButtonAction}
size="large"
color="secondary"
sx={{
"&&&": {
mt: !props.blockButton ? 2 : 0.5,
mb: !props.blockButton ? 4 : 0,
mr: !props.blockButton ? 1 : 0,
...buttonSx,
},
}}
{...restSubmitButtonProps}
>
{t("CANCEL")}
</Button>
)}
<SubmitButton
sx={{
"&&&": {
mt: 2,
...buttonSx,
},
}}
buttonText={props.buttonText}
loading={loading}
{...restSubmitButtonProps}
/>
</FlexWrapper>
</form>
)}
</Formik>
);
}