XGitHub

How It Works

A scannable reference for numora-react's event handlers, sanitization steps, formatting stages, and value output. For the long-form narrative with diagrams and demos, see Anatomy of a Numeric Input.

React integration

NumoraInput is a forwardRef functional component that calls the core event handlers directly - no vanilla class is instantiated. Props map to core options, and onChange fires after every pipeline cycle with a synthetic event whose target exposes both the raw and formatted values.

Events

numora-react intercepts four input vectors on a single <input type="text">. See Anatomy: event pipeline for the lifecycle and timing of each.

  • keydown - skips the cursor over thousand separators on Delete / Backspace.
  • beforeinput - primary formatting hook. Registered via native addEventListener (see why it bypasses React's synthetic system), calls preventDefault() and writes the formatted result via setRangeText, preserving the browser's undo stack.
  • input - single onChange emitter; idempotent on values already produced by the pipeline.
  • paste - splices clipboard text into the selection range, runs the full pipeline, repositions the cursor.
  • focus / blur - strip / re-apply thousand separators in FormatOn.Blur mode (the React default).

Sanitization

Seven steps run in order on every value before formatting is applied. See Anatomy: sanitization for the visual pipeline and per-step demos.

  1. Mobile keyboard artifact filtering
  2. Thousand separator removal
  3. Compact notation expansion (opt-in via enableCompactNotation)
  4. Scientific notation expansion
  5. Non-numeric character removal
  6. Extra decimal separator removal
  7. Leading zero normalization (disable with enableLeadingZeros)

Per-step configuration: see features/sanitization.

Formatting

Four stages run after sanitization. See Anatomy: formatting for diagrams and demos.

  1. Decimal trimming to decimalMaxLength
  2. Decimal padding to decimalMinLength
  3. Thousand grouping (FormatOn.Change only; deferred to blur in FormatOn.Blur)
  4. Cursor restoration after the formatted value replaces input.value

Value output

Every value is emitted as a string on a single onChange event, with both representations exposed on e.target. See Anatomy: raw vs formatted for the mental model, and Anatomy: the Proxy on e.target for how both values reach you from the same event.

  • raw - the sanitized string without separators; always returned as e.target.value. Safe to pass directly to form libraries.
  • formatted - the display string with thousand separators; written to input.value and available as e.target.formattedValue via NumoraHTMLInputElement.

Full pipeline

user action
    ├─ keydown ──────────── skip cursor over thousand separator (Delete/Backspace only)
    ├─ beforeinput ─────── decimal separator handling        [native addEventListener]
    │                      character insertion / deletion     (primary path)
    │                      e.preventDefault() + setRangeText
    │                      ↓ fires synchronous 'input' → React onChange (handleChange)
    ├─ paste ────────────── splice clipboard into selection    (dedicated handler)
    │                      sanitize + format + cursor reposition
    │                      ↓ synthetic ChangeEvent → onChange directly
    ├─ input / onChange ─── pure emitter                      (handleChange)
    │                      reads already-formatted value from DOM
    │                      strips separators → raw value
    │                      fires onChange (e.target.value = raw)
    │                      (also fires for undo/redo via native browser input event)
    │   (beforeinput pipeline produces:)
    │                      ▼
    │             trimToDecimalMaxLength
    │                      │
    │                      ▼
    │                ensureMinDecimals
    │                      │
    │                      ▼
    │            formatNumoraInput (thousand grouping)
    │            [FormatOn.Change only; skipped in FormatOn.Blur]
    │                      │
    │                ┌─────┴──────────────────────┐
    │                ▼                             ▼
    │           formatted                         raw
    │         → input.value                     → e.target.value (onChange)
    │         → e.target.formattedValue
    └─ blur ─────────────── formatValueForDisplay (FormatOn.Blur only)
                            → input.value