Skip to content

Replace otp.js with a Rust implementation using web_sys #26

@max-wells

Description

@max-wells

Context

public/components/otp.js is a vanilla JS OTP input controller (~100 lines) loaded as a <script> tag directly inside the InputOTP component (app_crates/registry/src/ui/input_otp.rs). It manipulates the DOM to sync a hidden <input> value with visible slot elements and manage focus/caret state.

What the current JS does

initContainer(root)   // bootstraps a single [data-otp-root] container
initAll()             // queries all containers and calls initContainer
MutationObserver      // watches for dynamically added OTP components (Leptos hydration)

The implementation:

  • Finds the hidden <input data-otp-input> inside the container
  • For each [data-otp-slot] (sorted by data-otp-index):
    • Updates [data-otp-char] text content with the character at that index
    • Sets data-active attribute based on cursor position
    • Shows/hides [data-otp-caret] depending on active + empty state
  • Adds a click listener on each slot → focuses the hidden input
  • Filters beforeinput to digits only
  • Listens to input, keydown, focus, blur → calls update()
  • Uses a MutationObserver on document.body to catch containers added after Leptos hydration

Goal

Replace otp.js with a pure Rust module using web_sys, eliminating the JS file and the <script> tag from InputOTP.

The Rust implementation should:

  • Use web_sys APIs: Document, Element, HtmlInputElement, EventListener, MutationObserver
  • Wire up all the same event listeners (click, beforeinput, input, keydown, focus, blur)
  • Replicate the update() logic (slot char sync, active state, caret visibility)
  • Handle post-hydration containers via MutationObserver (same as the JS version — see how lock_scroll.js was replaced in PR refactor(hooks): replace lock_scroll.js with Rust web_sys implementation #21 for reference)

Files involved

  • public/components/otp.js — the file to delete
  • app_crates/registry/src/ui/input_otp.rs — remove <script src="/components/otp.js" />, wire in the Rust init call
  • public/registry/styles/default/input_otp.md — update the registry snapshot

Reference

PR #21 replaced lock_scroll.js with Rust using the same pattern — good starting point.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions