InputOTP.tsx (2673B)
1 "use client" 2 3 import * as React from "react" 4 import { OTPInput, OTPInputContext } from "input-otp" 5 import { Dot } from "lucide-react" 6 7 import { cn } from "@/lib/utils" 8 9 const InputOTP = React.forwardRef< 10 React.ElementRef<typeof OTPInput>, 11 React.ComponentPropsWithoutRef<typeof OTPInput> 12 >(({ className, containerClassName, ...props }, ref) => ( 13 <OTPInput 14 ref={ref} 15 containerClassName={cn( 16 "flex items-center gap-2 has-[:disabled]:opacity-50", 17 containerClassName 18 )} 19 className={cn("disabled:cursor-not-allowed", className)} 20 {...props} 21 /> 22 )) 23 InputOTP.displayName = "InputOTP" 24 25 const InputOTPGroup = React.forwardRef< 26 React.ElementRef<"div">, 27 React.ComponentPropsWithoutRef<"div"> 28 >(({ className, ...props }, ref) => ( 29 <div ref={ref} className={cn("flex items-center", className)} {...props} /> 30 )) 31 InputOTPGroup.displayName = "InputOTPGroup" 32 33 const InputOTPSlot = React.forwardRef< 34 React.ElementRef<"div">, 35 React.ComponentPropsWithoutRef<"div"> & { index: number } 36 >(({ index, className, ...props }, ref) => { 37 const inputOTPContext = React.useContext(OTPInputContext) 38 const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] 39 40 return ( 41 <div 42 ref={ref} 43 className={cn( 44 "relative flex h-10 w-10 items-center justify-center border-y border-r border-purple-200/50 text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md text-white", 45 isActive && "z-10 ring-2 ring-ring ring-offset-background", 46 className 47 )} 48 {...props} 49 > 50 {char} 51 {hasFakeCaret && ( 52 <div className="pointer-events-none absolute inset-0 flex items-center justify-center"> 53 <div className="h-4 w-px animate-caret-blink bg-white duration-1000" /> 54 </div> 55 )} 56 </div> 57 ) 58 }) 59 InputOTPSlot.displayName = "InputOTPSlot" 60 61 const InputOTPSeparator = React.forwardRef< 62 React.ElementRef<"div">, 63 React.ComponentPropsWithoutRef<"div"> 64 >(({ ...props }, ref) => ( 65 <div ref={ref} role="separator" {...props}> 66 <Dot className="text-purple-200/50" /> 67 </div> 68 )) 69 InputOTPSeparator.displayName = "InputOTPSeparator" 70 71 function InputOTP6Digits({ ...props }) { 72 return ( 73 <InputOTP maxLength={6} {...props}> 74 <InputOTPGroup> 75 <InputOTPSlot index={0} /> 76 <InputOTPSlot index={1} /> 77 <InputOTPSlot index={2} /> 78 </InputOTPGroup> 79 <InputOTPSeparator /> 80 <InputOTPGroup> 81 <InputOTPSlot index={3} /> 82 <InputOTPSlot index={4} /> 83 <InputOTPSlot index={5} /> 84 </InputOTPGroup> 85 </InputOTP> 86 ) 87 } 88 89 export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator, InputOTP6Digits }