Skip to content

About

Headless component for multi-input PIN codes and verification codes

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

Example

Headless component for 2FA & PIN codes

Ever noticed the smooth interaction with Stripe's two factor authentication?

Entering a verification code is becoming a part of our users' daily routine.

Make it a delightful experience. Forget boring inputs, give your users what they deserve!

  • Next input autofocus
  • Input validation
  • Keyboard and mobile accessiblity
  • Paste handling
  • Completely unstyled
  • Zero markdown (only x- bindings)
  • Copy & Paste into your project

Click here for example

Requirements

Usage

Simply copy the following code to your project.

<div x-data="{ code: null }">
    <div x-bind="codePuncher" x-model="code">
        <input x-bind="digit" />
        <input x-bind="digit" />
        <input x-bind="digit" />
        <input x-bind="digit" />
    </div>
</div>

<script>
    document.addEventListener('alpine:init', () => {
        Alpine.bind('codePuncher', () => ({
            'x-modelable': 'glued',
            'x-data': () => ({
                digits: [],
                inputs: [],
                glued: '',
                init() {
                    this.inputs = Array.from(this.$root.querySelectorAll('[x-bind="digit"]'));
                    this.digits = Array(this.inputs.length).fill(null)

                    this.$watch('digits', (value) => this.glued = value.join(""))
                },
                clearCodeFrom(index) {
                    this.digits = this.digits.map((value, i) => i >= index ? null : value)
                },
                focusPreviousInput(index) {
                    this.$nextTick(() => this.inputs[index - 1].focus())
                },
                focusNextInput(index) {
                    this.$nextTick(() => this.$el.value && this.inputs[index + 1].focus())
                },
                handlePaste(index, event) {
                    const pastedDigits = String(event.clipboardData.getData('text/plain')).match(/\d/g) ?? []
                    const queuedDigits = pastedDigits.slice(0, this.digits.length - index)
                    const focusedInputIndex = Math.min(index + queuedDigits.length, this.digits.length - 1)

                    queuedDigits.forEach((value, i) => this.digits[index + i] = value)

                    this.$nextTick(() => this.inputs[focusedInputIndex].focus())
                },
                digit (index) {
                    return {
                        'x-data': `{
                            index: inputs.indexOf($el),
                            get first() { return this.index === 0 },
                            get last() { return this.index === this.digits.length - 1 },
                        }`,
                        'x-mask': '9',
                        'x-model': 'digits[index]',
                        'x-bind:disabled': 'index > glued.length',
                        'x-bind:tabindex': 'index < glued.length ? -1 : 0',
                        'x-on:focus': '!last && clearCodeFrom(index)',
                        'x-on:input': '!last && focusNextInput(index)',
                        'x-on:keydown.left': '!first && focusPreviousInput(index)',
                        'x-on:keydown.backspace': '!first && (last && $el.value ? $el.value = null : focusPreviousInput(index))',
                        'x-on:paste.prevent' : 'handlePaste(index, event)',
                        'inputmode': 'numeric',
                    }
                }
            })
        }))
    })
</script>

Styling

Since the component does not come with any styles, you will need to add yours. Feel free to alter the markdown in any way you need, just make sure the digits are inside the component. If you are looking for a stylized example click here.

About

Headless component for multi-input PIN codes and verification codes

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages