You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/pin-input/README.md

5.3 KiB

<PinInput> — eidos

Multi-cell OTP / verification code input. A transparent absolutely-positioned <input> captures every keystroke and paste; visible bordered cells follow the hidden input's caret. Field-aware (disabled / readonly / required / invalid OR-merge with the enclosing Field.Provider).

Usage

<script lang="ts">
  import { PinInput } from '$uix/eidos/components/pin-input';
  let code = $state('');
</script>

<PinInput
  bind:value={code}
  length={6}
  type="numeric"
  size="md"
  onComplete={(value) => console.log('filled:', value)}
>
  {#snippet children({ cells })}
    {#each cells as cell, i}
      <PinInput.Cell {cell} index={i} />
    {/each}
  {/snippet}
</PinInput>

Parts

Part Notes
PinInput (root) Provider; container + hidden capture input. Exposes { cells, isFocused, isHovering, value } via children snippet.
Cell One visible cell per character position. Receives cell (PinInputCellData) and index from the snippet.

Eidos-layer props

Prop Type Default Notes
size 'sm' | 'md' | 'lg' | 'xl' (responsive) 'md' Cell dimensions + glyph size.

Headless props (forwarded to soma)

value, onValueChange, onComplete, length, type, pattern, pasteTransformer, mask, placeholder, disabled, readonly, required, invalid, name, autocomplete, inputmode, aria-label. See src/uix/soma/components/pin-input/types.ts.

Cell data-* (emitted by morfo)

Attr When
data-active The cell holds the caret (focused + about to be typed in).
data-filled The cell has a character.
data-disabled Cascaded from the provider's disabled state.

Passive justification

Passive at the morfo level — pin-input's morfo declares parts (Provider, Cell, Input) but no events array. The user-perceived interactions (typing, paste, completion) are mediated by the hidden <input>'s native events; sema-side perceptual feedback piggy-backs on the commit family base when the consumer hooks onComplete.

The component IS interactive from the user's perspective. "Passive" here is a contract-layer classification, not a UX one — same logic as <AlertDialog> (delegates events to the underlying primitive).

Baseline

WAI-ARIA Textbox pattern: https://www.w3.org/WAI/ARIA/apg/patterns/textbox/

The OTP-input UX has no formal ARIA pattern. Soma follows the de-facto behaviour established by Radix-style implementations and the iOS / Android native code-input controls:

  • Single hidden <input> (not one input per cell) so paste, IME and autofill (one-time-code) work naturally.
  • Paste distributes across cells via the morfo's pasteTransformer hook.
  • Backspace clears the previous cell when the current one is empty.
  • Numeric type sets inputmode='numeric' + autocomplete='one-time-code' so iOS / Android offer the SMS-autofill banner.

Comparativa

Lib Single hidden input Mask Paste distribute Type presets Custom pattern Snippet cells
Radix Primitives n/a (not provided) — — — — —
Bits UI ✓ ✓ ✓ numeric/alphanumeric/alphabetic ✓ ✓
Ark UI input per cell ✓ ✓ numeric/alphanumeric — —
Chakra v3 input per cell ✓ ✓ numeric/alphanumeric — —
shadcn/ui (input-otp) ✓ ✓ ✓ numeric (default) ✓ ✓
Eidos (this) ✓ ✓ ✓ numeric / alphanumeric / alphabetic / custom ✓ ✓

Aligned with the shadcn / Bits family (single hidden input). Ark and Chakra use one input per cell which works but loses paste / IME quality.

Decisiones

  • Snippet-driven Cell rendering. Forces the consumer to iterate cells and render <PinInput.Cell /> per index. Auto-rendering would hide the per-index index arg which downstream consumers use for analytics / focus targeting.
  • Cell consumes data-active for the focus ring, not a literal <input:focus>. Only the hidden input is focused; the visible cells receive a synthetic focus ring via data-active to match the caret position.
  • No variant knob. Pin inputs are a singular visual pattern (bordered boxes); variants like outline-only / soft / ghost noise the contract. Consumers can recolor cells via data-color if needed.
  • Size as t-shirt (sm / md / lg / xl). Matches the rest of UIX's input scale.

Gaps

Gap Disposición Detalle
Group separator (e.g. 123-456) diferir Soma's pasteTransformer strips dashes; rendering a visible separator between groups (Bits' <PinInput.Group> part) isn't in the morfo. Add when a real consumer needs it.
Animated caret transition between cells diferir Soma emits data-active instantly; a transitioned bar could be a nice polish.
data-color cascade diferir Coloured cells (success on complete, danger on invalid) would propagate from the root's data-color. Add when the consumer pattern stabilises.

Reference

Powered by TurnKey Linux.