UNPKG

vue-input-otp

Version:

https://github.com/wobsoriano/vue-input-otp/assets/13049130/c5080f41-f411-4d38-aa57-d04d90c832c3

215 lines (166 loc) 5.37 kB
# OTP Input for Vue https://github.com/wobsoriano/vue-input-otp/assets/13049130/c5080f41-f411-4d38-aa57-d04d90c832c3 One-time passcode Input. Accessible & unstyled. Based on the [React version](https://github.com/guilhermerodz/input-otp) by [guilhermerodz](https://github.com/guilhermerodz). ## Installation ```bash npm install vue-input-otp ``` ## Usage ```vue <script setup lang="ts"> import { OTPInput } from 'vue-input-otp' import Slot from './Slot.vue' const input = ref('123456') </script> <template> <form> <OTPInput v-slot="{ slots }" v-model="input" :maxlength="6"> <!-- slots --> </OTPInput> </form> </template> ``` ## Default example The example below uses `tailwindcss`, `shadcn-vue`, `tailwind-merge` and `clsx`: ```vue <script setup lang="ts"> import { OTPInput } from 'vue-input-otp' import Slot from './Slot.vue' </script> <template> <OTPInput v-slot="{ slots }" :maxlength="6" container-class="group flex items-center has-[:disabled]:opacity-30" > <div class="flex"> <Slot v-for="(slot, idx) in slots.slice(0, 3)" v-bind="slot" :key="idx" /> </div> <!-- Fake Dash. Inspired by Stripe's MFA input. --> <div class="flex w-10 justify-center items-center"> <div class="w-3 h-1 rounded-full bg-border" /> </div> <div class="flex"> <Slot v-for="(slot, idx) in slots.slice(3)" v-bind="slot" :key="idx" /> </div> </OTPInput> </template> ``` ```vue <script setup lang="ts"> import type { SlotProps } from 'vue-input-otp' import { cn } from '$lib/utils' defineProps<SlotProps>() </script> <template> <div :class="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-accent-foreground/20 group-focus-within:border-accent-foreground/20', 'outline outline-0 outline-accent-foreground/20', { 'outline-4 outline-accent-foreground': isActive }, )" > <div v-if="char !== null"> {{ char }} </div> <!-- Emulate a Fake Caret --> <div v-if="char === null && isActive" class="absolute pointer-events-none inset-0 flex items-center justify-center animate-caret-blink"> <div class="w-px h-8 bg-white" /> </div> </div> </template> ``` ```ts import type { ClassValue } from 'clsx' // tailwind.config.ts for the blinking caret animation. // Small utility to merge class names. import { clsx } from 'clsx' import { twMerge } from 'tailwind-merge' const config = { theme: { extend: { keyframes: { 'caret-blink': { '0%,70%,100%': { opacity: '1' }, '20%,50%': { opacity: '0' }, }, }, animation: { 'caret-blink': 'caret-blink 1.2s ease-out infinite', }, }, }, } export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } ``` ## How it works There's currently no native OTP/2FA/MFA input in HTML, which means people are either going with 1. a simple input design or 2. custom designs like this one. This library works by rendering an invisible input as a sibling of the slots, contained by a `relative`ly positioned parent (the container root called OTPInput). ## API Reference ### OTPInput The root container. Define settings for the input via props. Then, pass in child elements to create the slots. ##### Props |Name|Description|Type|Values|Default| |:----|:----|:----|:----|:----| |`maxlength`|The number of slots.|`number`|`-`|`-`| |`containerClass`|The class for the root container.|`string`|`-`|`-`| |`textAlign`|Where is the text located within the input. Affects click-holding or long-press behavior.|`string`|`left`, `right`, `center`|`center`| |`inputmode`|Virtual keyboard appearance on mobile.|`string`|`numeric`, `text`|`numeric`| |`pushPasswordManagerStrategy`|Detect Password Managers and shift their badges to the right side, outside the input.|`string`|`increase-width`, `none`|`increase-width`| #### Slots |Name|Description|Props| |:----|:----|:----| |`default`|The slots to be rendered.|`slots: SlotProps[], isFocused: boolean, isHovering: boolean`| #### Events |Name|Description|Parameters| |:----|:----|:----| |`complete`|Emitted when the input is complete.|`value: string`| ## Examples <details> <summary>Automatic form submission on OTP completion</summary> ```vue <script setup> import { ref } from 'vue' import { OTPInput } from 'vue-input-otp' const formRef = ref() const buttonRef = ref() function onComplete() { // ... automatically submit the form formRef.value.submit() // ... or focus the button like as you wish buttonRef.value.focus() } </script> <template> <form ref="formRef"> <OTPInput @complete="onComplete" /> <button ref="buttonRef"> Submit </button> </form> </template> ``` </details> <details> <summary>Automatically focus the input when the page loads</summary> ```vue <script setup> import { OTPInput } from 'vue-input-otp' </script> <template> <form> <!-- Pro tip: accepts all common HTML input props... --> <OTPInput auto-focus /> </form> </template> ``` </details> ## Caveats See list of caveats in the original implementation [here](https://github.com/guilhermerodz/input-otp/blob/master/README.md#caveats). ## License MIT