input-otp.tsx 2.1 KB

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