Skip to content

Commit bb82718

Browse files
committed
poc: autofocus working
1 parent d4aced9 commit bb82718

File tree

1 file changed

+73
-37
lines changed

1 file changed

+73
-37
lines changed

apps/test/src/components/base-input.tsx

+73-37
Original file line numberDiff line numberDiff line change
@@ -9,44 +9,80 @@ export function BaseOTPInput(
99
const [value, setValue] = React.useState('')
1010
const [disabled, setDisabled] = React.useState(false)
1111

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+
1254
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+
))}
4573
</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>
5187
)
5288
}

0 commit comments

Comments
 (0)