damus.io

damus.io website
git clone git://jb55.com/damus.io
Log | Files | Refs | README | LICENSE

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 }