35 lines
1.4 KiB
PHP
35 lines
1.4 KiB
PHP
@props([
|
|
'name' => 'code',
|
|
'length' => 6,
|
|
'label' => null,
|
|
])
|
|
{{-- Segmented one-time-code input. Alpine keeps the visible segments; a hidden
|
|
field carries the joined value for the native form POST — or, inside a
|
|
Livewire form, for wire:model. wire:model needs both the attribute (so
|
|
Livewire's directive scan finds it — hence spreading $attributes here) and
|
|
a real "input" event: Alpine's :value binding only ever writes the DOM
|
|
property, which fires nothing on its own. --}}
|
|
<div
|
|
x-data="otpInput({ length: {{ (int) $length }} })"
|
|
class="flex gap-2"
|
|
role="group"
|
|
@if ($label) aria-label="{{ $label }}" @endif
|
|
>
|
|
<input type="hidden" name="{{ $name }}" :value="value" x-effect="$el.dispatchEvent(new Event('input', { bubbles: true }))" {{ $attributes }}>
|
|
<template x-for="(digit, i) in digits" :key="i">
|
|
<input
|
|
type="text"
|
|
inputmode="numeric"
|
|
autocomplete="one-time-code"
|
|
maxlength="1"
|
|
class="size-12 rounded border border-line bg-surface text-center font-mono text-lg text-ink"
|
|
:aria-label="`{{ $label ?? __('auth.otp_digit') }} ${i + 1}`"
|
|
x-model="digits[i]"
|
|
x-ref="cell"
|
|
@input="onInput(i, $event)"
|
|
@keydown.backspace="onBackspace(i, $event)"
|
|
@paste.prevent="onPaste($event)"
|
|
>
|
|
</template>
|
|
</div>
|