@@ -9,44 +9,80 @@ export function BaseOTPInput(
9
9
const [ value , setValue ] = React . useState ( '' )
10
10
const [ disabled , setDisabled ] = React . useState ( false )
11
11
12
+
13
+ function Slot ( props : any ) {
14
+ return (
15
+ < div
16
+ className = { cn (
17
+ 'relative w-10 h-14 text-[2rem]' ,
18
+ 'flex items-center justify-center' ,
19
+ 'transition-all duration-300' ,
20
+ 'border-border border-y border-r first:border-l first:rounded-l-md last:rounded-r-md' ,
21
+ 'group-hover:border-border/60 group-focus-within:border-border/60' ,
22
+ 'outline outline-0 outline-border/70' ,
23
+ { 'outline-4 outline-border' : props . isActive }
24
+ ) }
25
+ >
26
+ < div className = "group-has-[input[data-input-otp-placeholder-shown]]:opacity-20" >
27
+ { props . char ?? props . placeholderChar }
28
+ </ div >
29
+ { props . hasFakeCaret && < FakeCaret /> }
30
+ </ div >
31
+ ) ;
32
+ }
33
+
34
+ // Component to emulate a blinking caret
35
+ function FakeCaret ( ) {
36
+ return (
37
+ < div className = "absolute pointer-events-none inset-0 flex items-center justify-center animate-caret-blink" >
38
+ < div className = "w-px h-8 bg-foreground" />
39
+ </ div >
40
+ ) ;
41
+ }
42
+
43
+ // Component for the dash separator between groups of OTP slots
44
+ function FakeDash ( ) {
45
+ return (
46
+ < div className = "flex w-10 justify-center items-center" >
47
+ < div className = "w-3 h-1 rounded-full bg-border" />
48
+ </ div >
49
+ ) ;
50
+ }
51
+
52
+ const containerRef = React . useRef < HTMLDivElement > ( null ) ;
53
+
12
54
return (
13
- < OTPInput
14
- // Normal props
15
- value = { value }
16
- onChange = { setValue }
17
- disabled = { disabled }
18
- containerClassName = { cn ( 'group flex items-center' , {
19
- 'opacity-50' : disabled ,
20
- } ) }
21
- maxLength = { 6 }
22
- render = { ( { slots, isFocused, isHovering } ) => (
23
- < div
24
- className = { cn ( 'flex items-center gap-1' , {
25
- 'opacity-50' : overrideProps . disabled ?? disabled ,
26
- } ) }
27
- data-testid = "input-otp-renderer"
28
- data-test-render-is-hovering = { isHovering ? 'true' : undefined }
29
- data-test-render-is-focused = { isFocused ? 'true' : undefined }
30
- >
31
- { slots . map ( ( slot , idx ) => (
32
- < div
33
- data-testid = { `slot-${ idx } ` }
34
- data-test-char = { slot . char ?? undefined }
35
- key = { idx }
36
- className = { cn (
37
- 'transition-all duration-300 rounded-md border-black bg-white text-black w-10 h-14 border-[4px]' ,
38
- {
39
- 'border-[green]' : isFocused ,
40
- 'border-[red]' : slot . isActive ,
41
- } ,
42
- ) }
43
- >
44
- { slot . char !== null ? slot . char : ' ' }
55
+ < div ref = { containerRef } >
56
+ < OTPInput
57
+ // Normal props
58
+ value = { value }
59
+ onChange = { setValue }
60
+ disabled = { disabled }
61
+ containerClassName = { cn ( 'group flex items-center' , {
62
+ 'opacity-50' : disabled ,
63
+ } ) }
64
+ maxLength = { 6 }
65
+ autoFocus
66
+ onComplete = { ( ) => console . log ( 'complete' ) }
67
+ render = { ( { slots } ) => (
68
+ < >
69
+ < div className = "flex" >
70
+ { slots . slice ( 0 , 3 ) . map ( ( slot , idx ) => (
71
+ < Slot key = { idx } { ...slot } />
72
+ ) ) }
45
73
</ div >
46
- ) ) }
47
- </ div >
48
- ) }
49
- { ...overrideProps }
50
- />
74
+
75
+ < FakeDash />
76
+
77
+ < div className = "flex" >
78
+ { slots . slice ( 3 ) . map ( ( slot , idx ) => (
79
+ < Slot key = { idx } { ...slot } />
80
+ ) ) }
81
+ </ div >
82
+ </ >
83
+ ) }
84
+ { ...overrideProps }
85
+ />
86
+ </ div >
51
87
)
52
88
}
0 commit comments