Skip to content

Instantly share code, notes, and snippets.

@danestves
Created July 31, 2023 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danestves/eca51977bdd91846ae4c307de6eda91a to your computer and use it in GitHub Desktop.
Save danestves/eca51977bdd91846ae4c307de6eda91a to your computer and use it in GitHub Desktop.
otp.tsx
import type { OtpInputProps } from "./otp-input.types"
import * as React from "react"
function OtpInput({ size = 4, validationPattern = /[0-9]{1}/, value, onChange, ...props }: OtpInputProps) {
const inputRefs = React.useRef<(HTMLInputElement | null)[]>(new Array(size).fill(null))
const handleChange = (e: React.ChangeEvent<HTMLInputElement>, index: number) => {
const val = e.target.value
// Check if the value is valid
if (!validationPattern.test(val) && val !== "") return
// Change the value of the upper state using onChange
const newVal = inputRefs.current
.map((ref) => ref?.value ?? "")
.join("")
.slice(0, size)
onChange(newVal)
// Focus the next element if there's a value
if (val && index < size - 1) {
inputRefs.current[index + 1]?.focus()
}
}
const handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>, index: number) => {
if (e.key === "ArrowLeft" || e.key === "Backspace") {
inputRefs.current[index - 1]?.focus()
inputRefs.current[index - 1]?.setSelectionRange(0, 1)
return
}
if (e.key === "ArrowRight") {
inputRefs.current[index + 1]?.focus()
inputRefs.current[index + 1]?.setSelectionRange(0, 1)
return
}
}
const handleFocus = (e: React.FocusEvent<HTMLInputElement>, index: number) => {
inputRefs.current[index]?.focus()
inputRefs.current[index]?.setSelectionRange(0, 1)
}
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
e.preventDefault()
const val = e.clipboardData.getData("text").substring(0, size)
onChange(val)
}
const arr = new Array(size).fill("-")
return (
<>
{arr.map((_, index) => {
return (
<input
autoComplete="one-time-code"
inputMode="numeric"
key={index}
maxLength={1}
pattern={validationPattern.source}
type="text"
{...props}
onChange={(e) => handleChange(e, index)}
onFocus={(e) => handleFocus(e, index)}
onKeyUp={(e) => handleKeyUp(e, index)}
onPaste={handlePaste}
ref={(el) => (inputRefs.current[index] = el)}
/>
)
})}
</>
)
}
export default OtpInput
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment