Commit 505dc60
authored
[Feature] Add InputOtp component (#456)
* [Documentation] Add InputOtp design spec
Design doc for a Phlex/Stimulus port of shadcn's InputOTP, covering
the single-hidden-input architecture, compound component API, and
keyboard/paste behavior requirements.
* [Documentation] Add InputOtp implementation plan
Task-by-task TDD plan covering the compound component, Stimulus
controller, and docs-site wiring.
* [Feature] Add InputOtp root component
* [Feature] Add InputOtpGroup and InputOtpSeparator
* [Feature] Add InputOtpSlot
* [Feature] Add InputOtp Stimulus controller
* [Documentation] Add InputOtp docs template
* [Feature] Wire InputOtp into the docs site
Adds the route, controller action, sidebar/components-list entry,
Stimulus controller registration, and regenerates llms.txt/
llms-full.txt/sitemap.xml + the MCP registry to include InputOtp.
* [Bug Fix] Move ruby-ui--input-otp controller to the wrapper div
data-controller was on the real <input>, a leaf element, so the
sibling slot divs (the controller's slot targets) were outside its
scope and never got painted. Moving controller+values to the
outer wrapper brings the input and all slots into the same subtree.
Found via manual browser verification (Task 7) — typing didn't
update any slot.
* [Bug Fix] Fix keyboard replace-when-full and cross-browser focus ring
Manual testing surfaced two bugs:
- Arrow-key navigation selected a collapsed caret instead of a
1-character range, so typing a digit over a filled slot did
nothing (native replace-selection only works with a real range).
- Safari kept its native focus ring on the transparent input despite
outline-none; add appearance-none/shadow-none/focus:ring-0 to kill
it across browsers.
Also adds aria-invalid styling to InputOtpSlot (ported from upstream
shadcn, missed in the first pass) and expands the docs page with
Composition, Pattern, Four digits, Disabled, and Invalid examples
mirroring shadcn's InputOTP docs.
* [Feature] Match shadcn's InputOTP example set and fix flex layout bug
Fetched shadcn's actual example sources (Composition, Pattern, Form,
etc.) via github raw + rendered the live docs page in Chrome to
compare visually instead of relying on text alone. Adds the Form
example (Card + resend button + bigger slots via class: + fallback
links), matching https://ui.shadcn.com/docs/components/radix/input-otp#form.
Fixed a real layout bug found in the process: the wrapper div was
inline-flex instead of flex (shadcn/upstream uses flex), so an
inline sibling like the "I no longer have access" link rendered on
the same line as the slots instead of wrapping below them.
* [Bug Fix] Normalize selection after every input so replace-and-advance chains
After replacing a filled slot, the browser leaves a collapsed caret
right after the edited character. Since the value is at (or stays
at) maxlength, further typing with a collapsed caret and no
selection is a no-op — the user has to press an arrow key again
between every replacement.
Fix: after onInput, re-select the character at the new caret
position as a 1-char range whenever one exists there (i.e. we're
not in true insert-mode at the end of a not-yet-full value). This
mirrors the onKeydown/onPaste selection logic so typing keeps
replacing forward without needing arrow keys in between.
* [Bug Fix] Rebuild MCP registry after post-review InputOtp fixes
* [Bug Fix] Address cubic review: sanitize prefilled value, fix docs typo
- connect() now runs the same filter+truncate as onInput before the
first paint, so a server-rendered value: that exceeds length or
contains characters outside pattern can't hide behind the slots
and get submitted as-is.
- Fix "length:." double-punctuation typo in the Four digits example.
- Note in the plan doc that its Task 4 controller snapshot predates
later bug-fix commits (normalizeSelection, arrow-key range
selection) — cubic flagged both as "missing" because it reviewed
the plan, not gem/lib/ruby_ui/input_otp/input_otp_controller.js
where they already exist.1 parent c7fcc90 commit 505dc60
20 files changed
Lines changed: 1882 additions & 0 deletions
File tree
- design
- plans
- docs
- app
- controllers
- javascript/controllers
- ruby_ui
- lib
- views/docs
- config
- public
- gem
- lib/ruby_ui/input_otp
- test/ruby_ui
- mcp/data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 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 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
0 commit comments