DOM adapter
@telixon/web-sdk is the layer between a browser <input> and a headless
input controller. It listens for the native editing events the
browser fires, translates each into an editing intent the controller understands, and writes the
controller’s next state back to the element. The controller holds all phone-number logic; the
adapter holds only the DOM wiring.
Intercepting beforeinput
Section titled “Intercepting beforeinput”The adapter attaches a beforeinput listener and calls event.preventDefault() on every editing
event it handles. The browser’s own edit never runs; the adapter reads the current value,
selectionStart, and selectionEnd, dispatches the matching controller intent, and applies the
result. It classifies the event by its inputType:
inputType |
Controller intent |
|---|---|
insertText, insertReplacementText, insertFromPaste, insertFromDrop, insertFromYank |
insert with the event text over the selection |
deleteContentBackward, deleteByCut, deleteContent, deleteByDrag |
deleteBackward |
deleteContentForward |
deleteForward |
deleteWordBackward |
deleteBackward over the previous word boundary |
deleteWordForward |
deleteForward over the next word boundary |
deleteSoftLineBackward, deleteHardLineBackward |
deleteBackward from the line start to the caret |
deleteSoftLineForward, deleteHardLineForward |
deleteForward from the caret to the line end |
deleteEntireSoftLine, deleteEntireHardLine |
deleteBackward over the whole value |
historyUndo |
undo, when an undo step is available |
historyRedo |
redo, when a redo step is available |
Insert events cover typing, paste, drop, and dictation replacement. When event.data is absent, as
with a paste or drop, the text is read from event.dataTransfer instead; an empty result inserts
nothing. Word deletes resolve their boundary from the current value: with a collapsed caret the
adapter walks past adjacent separators and then over the digit group, so a word delete always
consumes a run of digits rather than a stray separator.
Any inputType not in the table falls through untouched. Because the event was prevented, an
unhandled edit is a no-op rather than a raw character reaching the field.
Undo and redo
Section titled “Undo and redo”Undo and redo reach the controller two ways. Browsers that emit historyUndo and historyRedo
through beforeinput route through the table above. As a fallback, the adapter also listens for the
Ctrl/Cmd keyboard shortcuts (Z, Shift+Z, Y) and maps them to the controller’s undo and
redo. Either path replays the controller’s own history, so the platform gesture and the
programmatic undo()/redo() methods restore the same states, caret included.
IME composition
Section titled “IME composition”While an input method editor is composing, the adapter stays out of the way. beforeinput events
carry isComposing: true during composition, and the adapter ignores them: the intermediate
composition string is not phone input and must not be reformatted mid-stroke. When composition
finishes, the compositionend event delivers the committed text, and the adapter inserts that text
as a single insert intent over the selection recorded on the element. Composition that commits no
text produces no edit.
Editing intents, not value rewrites
Section titled “Editing intents, not value rewrites”The adapter never lets the browser mutate input.value and never diffs an old string against a new
one. It reports the operation the user performed, the current value, the inserted or deleted text,
and the selection, and the controller returns the next state. The adapter’s only write-back is to
set input.value and the caret range from that state.
Routing intents rather than reformatting a settled string is what keeps the caret exact. The
controller tracks the caret in digit space and maps it back through the format mask, so it knows
where the caret belongs after a reflow. A diff of the rendered string could not recover that, since
reformatting moves separators the user never touched. This is why paste and drop go through insert
with the transferred text rather than being written to the field directly.
Single attachment
Section titled “Single attachment”One phone input attaches per element. The adapter records each attached element in a WeakSet, and
a second createPhoneInput on the same element throws. destroy() removes the element from the set
and detaches every listener, so the same element can be attached again afterward. destroy() is
idempotent.
The element must be an <input type="text"> or <input type="tel">; any other type throws.
Construction also requires a ready engine, so call ensureEngineReady() before createPhoneInput.
What stays consumer-owned
Section titled “What stays consumer-owned”The adapter writes exactly two things to the element: value and the selection range. Everything
else is data on the emitted PhoneInputState for the host to render.
- The placeholder is exposed as
state.placeholderand never written to the element’splaceholderattribute. See Placeholders. - Styling, class names, and the field’s visual state are yours. The package ships no CSS.
- Labels, the region picker, and any surrounding markup are yours.
state.region,state.validationError, and the filter values are the inputs to that rendering.
Next steps
Section titled “Next steps”- Modes covers what each mode resolves and displays.
- Input controller describes the editing model the adapter drives.
- Programmatic control covers the methods on the returned input.