11import { Form , FormControl , FormField , FormItem } from "../ui/form" ;
2- import {
3- InputOTP ,
4- InputOTPGroup ,
5- InputOTPSeparator ,
6- InputOTPSlot ,
7- } from "../ui/input-otp" ;
2+ import { Input } from "../ui/input" ;
83import { zodResolver } from "@hookform/resolvers/zod" ;
94import { useForm } from "react-hook-form" ;
105import { totpSchema , TotpSchema } from "@/schemas/totp-schema" ;
116import { useTranslation } from "react-i18next" ;
7+ import { useRef } from "react" ;
128import z from "zod" ;
139
1410interface Props {
@@ -19,6 +15,7 @@ interface Props {
1915export const TotpForm = ( props : Props ) => {
2016 const { formId, onSubmit } = props ;
2117 const { t } = useTranslation ( ) ;
18+ const autoSubmittedRef = useRef ( false ) ;
2219
2320 z . config ( {
2421 customError : ( iss ) =>
@@ -29,14 +26,19 @@ export const TotpForm = (props: Props) => {
2926 resolver : zodResolver ( totpSchema ) ,
3027 } ) ;
3128
32- const handleChange = ( value : string ) => {
33- form . setValue ( "code" , value , { shouldDirty : true , shouldValidate : true } ) ;
34-
35- if ( value . length === 6 ) {
36- onSubmit ( { code : value } ) ;
29+ const handleChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
30+ const value = e . target . value . replace ( / \D / g, "" ) . slice ( 0 , 6 ) ;
31+ form . setValue ( "code" , value , { shouldDirty : true , shouldValidate : false } ) ;
32+ if ( value . length === 6 && ! autoSubmittedRef . current ) {
33+ autoSubmittedRef . current = true ;
34+ form . handleSubmit ( onSubmit ) ( ) ;
35+ return ;
3736 }
37+ autoSubmittedRef . current = false ;
3838 } ;
3939
40+ // Note: This is not the best UX, ideally we would want https://github.com/guilhermerodz/input-otp
41+ // but some password managers cannot autofill the inputs (see #92) so, simple input it is
4042 return (
4143 < Form { ...form } >
4244 < form id = { formId } onSubmit = { form . handleSubmit ( onSubmit ) } >
@@ -46,25 +48,17 @@ export const TotpForm = (props: Props) => {
4648 render = { ( { field } ) => (
4749 < FormItem >
4850 < FormControl >
49- < InputOTP
50- maxLength = { 6 }
51+ < Input
5152 { ...field }
53+ type = "text"
54+ inputMode = "numeric"
5255 autoComplete = "one-time-code"
5356 autoFocus
57+ maxLength = { 6 }
58+ placeholder = "XXXXXX"
5459 onChange = { handleChange }
55- >
56- < InputOTPGroup >
57- < InputOTPSlot index = { 0 } />
58- < InputOTPSlot index = { 1 } />
59- < InputOTPSlot index = { 2 } />
60- </ InputOTPGroup >
61- < InputOTPSeparator />
62- < InputOTPGroup >
63- < InputOTPSlot index = { 3 } />
64- < InputOTPSlot index = { 4 } />
65- < InputOTPSlot index = { 5 } />
66- </ InputOTPGroup >
67- </ InputOTP >
60+ className = "text-center"
61+ />
6862 </ FormControl >
6963 </ FormItem >
7064 ) }
0 commit comments