x
1
2
3
<form data-controller="web_otp" action="/lookbook/home/index" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="2rKOP8_dnTH2ETmuYRXU4wJHKKg-6FEjJOrsnvg_hiiuDESuRLNj7Ahf-3Qdbcu-NbBntVpZ6zveDOF4eyu3Ow" autocomplete="off" /> <input placeholder="6-digit code*" required="required" autocomplete="one-time-code" inputmode="numeric" pattern="\d{6}" class="input" data-web-otp-target="input" type="text" name="otp_code" id="otp_code" /></form>
1
2
3
<%= form_with url: nil, data: { controller: "web_otp" } do |form| %> <%= form.text_field :otp_code, placeholder: "6-digit code*", required: true, autocomplete: "one-time-code", inputmode: "numeric", pattern: "\\d{6}", class: "input", data: { web_otp_target: "input" } %><% end %>
CSS is not required or multiple files are needed, check the notes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Controller } from "@hotwired/stimulus"export default class extends Controller { static targets = [ "input" ] initialize() { this.abortController = new AbortController() } disconnect() { this.abortController.abort() } connect() { this.#supportsOTP && this.#requestOTP() } #requestOTP() { navigator.credentials.get(this.#options).then(credential => { this.inputTarget.value = credential.code this.element.requestSubmit() }).catch(error => {}) } get #options() { return { otp: { transport: ["sms"] }, signal: this.abortController.signal } } get #supportsOTP() { return "OTPCredential" in window }}