Created
September 1, 2025 19:24
-
-
Save andreasvirkus/9f0ab367e688e5cc1058e97473584efb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div class="otp-input my-4 flex justify-center gap-2 text-black"> | |
<input | |
v-for="i of 6" | |
:key="i" | |
v-model="otp[i - 1]" | |
type="tel" | |
maxlength="1" | |
class="otp-field text-heading" | |
required | |
@input="($event) => handleInput($event, i - 1)" | |
@paste="handlePaste" | |
@keydown.enter="handleSubmit" | |
@keydown.right="focusNext(i - 1)" | |
@keydown.left="focusPrev(i - 1)" | |
@keydown.delete="focusPrev(i - 1, true)" | |
/> | |
</div> | |
</template> | |
<script setup lang="ts"> | |
const emit = defineEmits<{ | |
(event: 'update', otp: string): void | |
}>() | |
const otp = ref(['', '', '', '', '', '']) | |
const otpToken = computed(() => otp.value.join('')) | |
watch(otpToken, (val) => { | |
if (val.length === 6) emit('update', val) | |
}) | |
const handleInput = (event: Event, i: number) => { | |
const val = (event.target as HTMLInputElement).value | |
if (isNaN(Number(val))) { | |
otp.value[i] = '' | |
return | |
} | |
if ((event.target as HTMLInputElement).value) focusNext(i) | |
} | |
const focusNext = (index: number) => { | |
const nextInput = document.querySelectorAll('.otp-field')[index + 1] | |
;(nextInput as HTMLElement)?.focus() | |
} | |
const focusPrev = (index: number, clearField = false) => { | |
if (clearField && !!otp.value[index]) { | |
otp.value[index] = '' | |
return | |
} | |
const prevInput = document.querySelectorAll('.otp-field')[index - 1] | |
;(prevInput as HTMLElement)?.focus() | |
} | |
const handlePaste = (event: ClipboardEvent) => { | |
const pastedData = event.clipboardData?.getData('text')?.trim() ?? '' | |
if (pastedData.length === 6) { | |
otp.value = pastedData.split('') | |
// Focus the last input field after pasting | |
;(document.querySelector('.otp-field:last-child') as HTMLElement)?.focus() | |
} | |
} | |
const handleSubmit = () => { | |
if (otpToken.value.length < otp.value.length) return | |
emit('update', otpToken.value) | |
} | |
</script> | |
<style> | |
.otp-field { | |
width: 40px; | |
height: 70px; | |
border-radius: 8px; | |
border: 2px solid #ccc; | |
text-align: center; | |
font-size: 40px; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment