Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 73 additions & 37 deletions apps/test/src/components/base-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,80 @@ export function BaseOTPInput(
const [value, setValue] = React.useState('')
const [disabled, setDisabled] = React.useState(false)


function Slot(props: any) {
return (
<div
className={cn(
'relative w-10 h-14 text-[2rem]',
'flex items-center justify-center',
'transition-all duration-300',
'border-border border-y border-r first:border-l first:rounded-l-md last:rounded-r-md',
'group-hover:border-border/60 group-focus-within:border-border/60',
'outline outline-0 outline-border/70',
{ 'outline-4 outline-border': props.isActive }
)}
>
<div className="group-has-[input[data-input-otp-placeholder-shown]]:opacity-20">
{props.char ?? props.placeholderChar}
</div>
{props.hasFakeCaret && <FakeCaret />}
</div>
);
}

// Component to emulate a blinking caret
function FakeCaret() {
return (
<div className="absolute pointer-events-none inset-0 flex items-center justify-center animate-caret-blink">
<div className="w-px h-8 bg-foreground" />
</div>
);
}

// Component for the dash separator between groups of OTP slots
function FakeDash() {
return (
<div className="flex w-10 justify-center items-center">
<div className="w-3 h-1 rounded-full bg-border" />
</div>
);
}

const containerRef = React.useRef<HTMLDivElement>(null);

return (
<OTPInput
// Normal props
value={value}
onChange={setValue}
disabled={disabled}
containerClassName={cn('group flex items-center', {
'opacity-50': disabled,
})}
maxLength={6}
render={({ slots, isFocused, isHovering }) => (
<div
className={cn('flex items-center gap-1', {
'opacity-50': overrideProps.disabled ?? disabled,
})}
data-testid="input-otp-renderer"
data-test-render-is-hovering={isHovering ? 'true' : undefined}
data-test-render-is-focused={isFocused ? 'true' : undefined}
>
{slots.map((slot, idx) => (
<div
data-testid={`slot-${idx}`}
data-test-char={slot.char ?? undefined}
key={idx}
className={cn(
'transition-all duration-300 rounded-md border-black bg-white text-black w-10 h-14 border-[4px]',
{
'border-[green]': isFocused,
'border-[red]': slot.isActive,
},
)}
>
{slot.char !== null ? slot.char : ' '}
<div ref={containerRef}>
<OTPInput
// Normal props
value={value}
onChange={setValue}
disabled={disabled}
containerClassName={cn('group flex items-center', {
'opacity-50': disabled,
})}
maxLength={6}
autoFocus
onComplete={() => console.log('complete')}
render={({ slots }) => (
<>
<div className="flex">
{slots.slice(0, 3).map((slot, idx) => (
<Slot key={idx} {...slot} />
))}
</div>
))}
</div>
)}
{...overrideProps}
/>

<FakeDash />

<div className="flex">
{slots.slice(3).map((slot, idx) => (
<Slot key={idx} {...slot} />
))}
</div>
</>
)}
{...overrideProps}
/>
</div>
)
}