<div class="flex gap-2 justify-center" :class="wrapperClass">
<template v-for="(input, index) in length" :key="index">
:id="`digit-${index + 1}`"
:ref="(el) => { inputs[index] = el as HTMLInputElement }"
class="w-12 h-12 text-center text-xl"
@focus="inputs[index].select()"
@mousedown.prevent="inputs[index].select()"
@input="onInput(index, $event as InputEvent)"
@paste.prevent.stop="onPaste(index, $event)"
@keydown.delete.prevent="onDelete(index)"
@keydown.left.prevent="onLeft(index)"
@keydown.right.prevent="onRight(index)"
import { nextTick, onMounted, reactive } from "vue";
const props = defineProps({
const inputs: Array<HTMLInputElement | null> = [];
const emits = defineEmits(["onfinish", "onchange"]);
const digits = reactive(Array.from({ length: props.length }, () => ""));
function onInput(index: number, event: InputEvent) {
digits[index] = event.data;
inputs[index + 1]?.focus();
function onPaste(index: number, event: ClipboardEvent) {
let paste = event.clipboardData?.getData("text") || "";
paste = paste.replace(/\D/g, "").slice(0, props.length - index);
for (let i = 0; i < paste.length; i++) {
digits[index + i] = paste[i];
// select next closest input after paste
const lastFilledIndex = Math.min(
index + paste.length - 1,
inputs[lastFilledIndex]?.focus();
function onDelete(index: number) {
inputs[index - 1]?.focus();
function onLeft(index: number) {
inputs[index - 1]?.focus();
function onRight(index: number) {
inputs[index + 1]?.focus();
const otp = digits.join("");
if (otp.length === props.length) {