68 lines
1.9 KiB
TypeScript
68 lines
1.9 KiB
TypeScript
import { useRef, useState } from "preact/hooks";
|
|
|
|
const mismatchMessage = "Passwords do not match.";
|
|
|
|
export default function PasswordConfirmationFields() {
|
|
const [password, setPassword] = useState("");
|
|
const [passwordConfirm, setPasswordConfirm] = useState("");
|
|
const confirmInput = useRef<HTMLInputElement>(null);
|
|
const passwordsDoNotMatch = passwordConfirm.length > 0 &&
|
|
password !== passwordConfirm;
|
|
|
|
function updatePassword(value: string) {
|
|
setPassword(value);
|
|
const confirmation = confirmInput.current;
|
|
if (confirmation) {
|
|
confirmation.setCustomValidity(
|
|
confirmation.value && value !== confirmation.value
|
|
? mismatchMessage
|
|
: "",
|
|
);
|
|
}
|
|
}
|
|
|
|
function updatePasswordConfirm(value: string) {
|
|
setPasswordConfirm(value);
|
|
confirmInput.current?.setCustomValidity(
|
|
value && password !== value ? mismatchMessage : "",
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<input
|
|
type="password"
|
|
name="password"
|
|
placeholder="Password"
|
|
class="input min-w-0 flex-1"
|
|
autocomplete="new-password"
|
|
minLength={8}
|
|
required
|
|
value={password}
|
|
onInput={(event) => updatePassword(event.currentTarget.value)}
|
|
/>
|
|
<div class="min-w-0 flex-1">
|
|
<input
|
|
ref={confirmInput}
|
|
type="password"
|
|
name="passwordConfirm"
|
|
placeholder="Confirm"
|
|
class="input w-full"
|
|
autocomplete="new-password"
|
|
minLength={8}
|
|
required
|
|
value={passwordConfirm}
|
|
aria-invalid={passwordsDoNotMatch}
|
|
aria-describedby="password-confirm-error"
|
|
onInput={(event) => updatePasswordConfirm(event.currentTarget.value)}
|
|
/>
|
|
{passwordsDoNotMatch && (
|
|
<p id="password-confirm-error" class="mt-1 text-sm text-error">
|
|
{mismatchMessage}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|