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