108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
/**
|
|
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
* See the LICENSE file for details.
|
|
*/
|
|
|
|
import type { FormEvent } from "react";
|
|
import { useMemo, useRef, useState } from "react";
|
|
import { observer } from "mobx-react";
|
|
// icons
|
|
import { CircleAlert, XCircle } from "lucide-react";
|
|
// types
|
|
import { Button } from "@plane/propel/button";
|
|
import type { IEmailCheckData } from "@plane/types";
|
|
// ui
|
|
import { Input, Spinner } from "@plane/ui";
|
|
// helpers
|
|
import { cn } from "@plane/utils";
|
|
import { checkEmailValidity } from "@/helpers/string.helper";
|
|
|
|
type TAuthEmailForm = {
|
|
defaultEmail: string;
|
|
onSubmit: (data: IEmailCheckData) => Promise<void>;
|
|
};
|
|
|
|
export const AuthEmailForm = observer(function AuthEmailForm(props: TAuthEmailForm) {
|
|
const { onSubmit, defaultEmail } = props;
|
|
// states
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [email, setEmail] = useState(defaultEmail);
|
|
|
|
const emailError = useMemo(
|
|
() => (email && !checkEmailValidity(email) ? { email: "Email is invalid" } : undefined),
|
|
[email]
|
|
);
|
|
|
|
const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault();
|
|
setIsSubmitting(true);
|
|
const payload: IEmailCheckData = {
|
|
email: email,
|
|
};
|
|
await onSubmit(payload);
|
|
setIsSubmitting(false);
|
|
};
|
|
|
|
const isButtonDisabled = email.length === 0 || Boolean(emailError?.email) || isSubmitting;
|
|
|
|
const [isFocused, setIsFocused] = useState(true);
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
|
|
return (
|
|
<form onSubmit={handleFormSubmit} className="mt-5 space-y-4">
|
|
<div className="space-y-1">
|
|
<label className="text-13 font-medium text-tertiary" htmlFor="email">
|
|
Email
|
|
</label>
|
|
<div
|
|
className={cn(
|
|
`relative flex items-center rounded-md border bg-surface-1`,
|
|
!isFocused && Boolean(emailError?.email) ? `border-danger-strong` : `border-subtle`
|
|
)}
|
|
onFocus={() => {
|
|
setIsFocused(true);
|
|
}}
|
|
onBlur={() => {
|
|
setIsFocused(false);
|
|
}}
|
|
>
|
|
<Input
|
|
id="email"
|
|
name="email"
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
placeholder="name@company.com"
|
|
className={`h-10 w-full border-0 disable-autofill-style placeholder:text-placeholder autofill:bg-danger-subtle focus:bg-none active:bg-transparent`}
|
|
autoComplete="off"
|
|
autoFocus
|
|
ref={inputRef}
|
|
/>
|
|
{email.length > 0 && (
|
|
<button
|
|
type="button"
|
|
aria-label="Clear email"
|
|
onClick={() => {
|
|
setEmail("");
|
|
inputRef.current?.focus();
|
|
}}
|
|
tabIndex={-1}
|
|
>
|
|
<XCircle className="h-10 w-11 stroke-placeholder px-3 text-11 hover:cursor-pointer" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
{emailError?.email && !isFocused && (
|
|
<p className="flex items-center gap-1 px-0.5 text-11 text-danger-primary">
|
|
<CircleAlert height={12} width={12} />
|
|
{emailError.email}
|
|
</p>
|
|
)}
|
|
</div>
|
|
<Button type="submit" variant="primary" className="w-full" size="xl" disabled={isButtonDisabled}>
|
|
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
|
|
</Button>
|
|
</form>
|
|
);
|
|
});
|