createInternationalInputController
createInternationalInputController creates an InputController whose value carries the calling code, so the region is inferred from what the user types instead of being fixed up front. Digits drive one deterministic automaton compiled from Google’s metadata; parsePhoneNumber and the input controller are the same shared resolve layer, queried in batch versus per keystroke.
This page documents construction and the behavior specific to international input. The returned controller’s methods, InputState fields, and history semantics are defined once on the InputController contract page.
Import
Section titled “Import”import { createInternationalInputController } from '@telixon/core';import type { InternationalDisplayConfig, InternationalInputControllerConfig, PlusPrefixMode } from '@telixon/core';Signature
Section titled “Signature”function createInternationalInputController(config: InternationalInputControllerConfig = {}): InputController;config is optional and defaults to {}.
// 'none' renders no plus, 'fixed' renders a permanent plus, 'erasable' renders a plus the user can delete and retype.export type PlusPrefixMode = 'none' | 'fixed' | 'erasable';
export type InternationalDisplayConfig = | { readonly callingCodeInInput: false } | { readonly callingCodeInInput: true; readonly plusPrefix: PlusPrefixMode };
export type InternationalInputControllerConfig = | { defaultRegion?: RegionCode; strict?: boolean; initialValue?: string; maxHistorySize?: number; display?: Extract<InternationalDisplayConfig, { callingCodeInInput: true }>; } | { defaultRegion: RegionCode; strict?: boolean; initialValue?: string; maxHistorySize?: number; display: Extract<InternationalDisplayConfig, { callingCodeInInput: false }>; };Config
Section titled “Config”The config is a discriminated union on display.callingCodeInInput. With callingCodeInInput: true (the default) the calling code lives inside the input value and plusPrefix chooses how the leading + renders. With callingCodeInInput: false the value holds only national digits, so defaultRegion becomes required; the union makes the compiler enforce this.
| Field | Type | Default | Meaning |
|---|---|---|---|
display |
InternationalDisplayConfig |
{ callingCodeInInput: true, plusPrefix: 'none' } |
Whether the calling code is part of the input value, and how the leading + renders. |
defaultRegion |
RegionCode |
none | Region that supplies the calling code. Optional when the calling code is in the input; required when it is not. |
strict |
boolean |
false |
Resolves only within defaultRegion. See Enforce strict mode. |
initialValue |
string |
'', or the defaultRegion calling code when the calling code is in the input |
Value the controller starts from. The starting state is the history root, so canUndo is false. |
maxHistorySize |
number |
150 |
Maximum number of undo history entries. |
Each PlusPrefixMode in one line:
'none': no plus is rendered; the value starts with the calling code digits.'fixed': the plus is always shown and cannot be edited away.'erasable': the plus is shown, deletable with backspace or forward delete at position 0, and restored by typing+.
Returns
Section titled “Returns”An InputController, the same contract createNationalInputController returns. getPhoneNumber() resolves the current value to a PhoneNumber.
import { createInternationalInputController, ensureEngineReady } from '@telixon/core';
await ensureEngineReady();
const controller = createInternationalInputController({ display: { callingCodeInInput: true, plusPrefix: 'fixed' },});
controller.setValue('+442079460958');
controller.currentState.value; // '+44 20 7946 0958'controller.currentState.region; // 'GB'controller.getPhoneNumber().formatE164(); // '+442079460958'Errors
Section titled “Errors”The factory throws EngineNotReadyError when called before the engine is initialized. Initialization is explicit; see ensureEngineReady.
import { createInternationalInputController, EngineNotReadyError } from '@telixon/core';
try { createInternationalInputController();} catch (error) { error instanceof EngineNotReadyError; // true}After construction, invalid input does not throw. Validity is an accepting state, and every ValidationError variant is a named way of not being in one: an empty value resolves to a PhoneNumber whose getValidationError() returns { kind: 'EMPTY' }.
Behavior
Section titled “Behavior”Region is inferred from the calling code
Section titled “Region is inferred from the calling code”Every keystroke feeds digits to the automaton, which consumes the calling code before the national number. While the calling code is partial, region reports the primary region of the current automaton state and can change with the next digit. Once the calling code completes, the value gains a separator space and the national digits format progressively in the inferred region’s international layout; when they complete a format pattern, the value matches formatInternational().
National digits keep narrowing the region across shared calling codes:
const controller = createInternationalInputController({ defaultRegion: 'CA', display: { callingCodeInInput: true, plusPrefix: 'fixed' },});
controller.setValue('+12125550198');controller.currentState.region; // 'US', not 'CA': the national digits are a New York numberThe plus is presentation only
Section titled “The plus is presentation only”With callingCodeInInput: true, digits are always resolved as calling code first, whatever the plus mode. Hiding the plus ('none') or erasing it ('erasable') never changes resolution:
const controller = createInternationalInputController(); // plusPrefix 'none'
controller.setValue('+442079460958');controller.currentState.value; // '44 20 7946 0958', 'none' renders no pluscontroller.getPhoneNumber().formatE164(); // '+442079460958'setValue infers plus visibility
Section titled “setValue infers plus visibility”With plusPrefix: 'erasable', setValue reads plus visibility from the string: a value starting with + shows the plus, a non-empty value without one starts with the plus erased, and '' resets to the visible plus. With 'fixed' the plus is always rendered; with 'none' it never is.
Calling code outside the input
Section titled “Calling code outside the input”With display: { callingCodeInInput: false } the value holds only national digits and the required defaultRegion supplies the calling code. setRegion re-seeds the calling code and re-resolves the digits already typed. getPhoneNumber() still resolves the full international number.
Default region seeds the initial value
Section titled “Default region seeds the initial value”With the calling code in the input, defaultRegion seeds the initial value with the region’s calling code, so the user starts typing after it. An explicit initialValue overrides the seed.
const controller = createInternationalInputController({ defaultRegion: 'US' });
controller.currentState.value; // '1 'controller.currentState.region; // 'US'Strict mode pins the region
Section titled “Strict mode pins the region”With strict: true, resolution never leaves defaultRegion: the reported region stays defaultRegion, and numbers that only match elsewhere are invalid. Full semantics are in Enforce strict mode.
const controller = createInternationalInputController({ defaultRegion: 'CA', strict: true, display: { callingCodeInInput: true, plusPrefix: 'fixed' },});
controller.setValue('+12125550198');controller.currentState.value; // '+1 212-555-0198'controller.currentState.region; // 'CA'controller.getPhoneNumber().isValid(); // false: a US number rejected under CAExamples
Section titled “Examples”Type digit by digit
Section titled “Type digit by digit”Each insert(value, text, selectionStart, selectionEnd) call mirrors one keystroke; the controller returns the next value, caret, and region.
const controller = createInternationalInputController({ display: { callingCodeInInput: true, plusPrefix: 'fixed' },});
controller.currentState.value; // '+'
let state = controller.currentState;
for (const digit of ['4', '4', '2', '0', '7', '9', '4', '6', '0', '9']) { state = controller.insert(state.value, digit, state.selectionStart, state.selectionEnd);}
// value after each keystroke:// '+4' region 'GB'// '+44 ' region 'GB', calling code complete// '+44 2'// '+44 20 '// '+44 20 7'// '+44 20 79'// '+44 20 794'// '+44 20 7946 '// '+44 20 7946 0'// '+44 20 7946 09'
state.region; // 'GB'Erase and restore the plus
Section titled “Erase and restore the plus”const controller = createInternationalInputController({ display: { callingCodeInInput: true, plusPrefix: 'erasable' },});
controller.currentState.value; // '+'
controller.setValue('+44 20 7946 0958');
// Backspace with the caret right after the plus erases only the plus.controller.deleteBackward('+44 20 7946 0958', 1, 1);controller.currentState.value; // '44 20 7946 0958'
// Typing '+' at position 0 restores it.controller.insert('44 20 7946 0958', '+', 0, 0);controller.currentState.value; // '+44 20 7946 0958'
// setValue infers plus visibility from the string it receives.controller.setValue('442079460958');controller.currentState.value; // '44 20 7946 0958'controller.getPhoneNumber().formatE164(); // '+442079460958'
controller.setValue('');controller.currentState.value; // '+'Keep the calling code out of the input
Section titled “Keep the calling code out of the input”The value stays national while the resolved number stays international. Pair this mode with a separate region control that calls setRegion.
const controller = createInternationalInputController({ defaultRegion: 'GB', display: { callingCodeInInput: false },});
controller.setValue('2079460958');controller.currentState.value; // '20 7946 0958'controller.currentState.region; // 'GB'controller.getPhoneNumber().formatE164(); // '+442079460958'
// setRegion re-seeds the calling code and re-resolves the same digits.controller.setValue('2125550198');controller.currentState.value; // '21 2555 0198'
controller.setRegion('US');controller.currentState.value; // '212-555-0198'controller.getPhoneNumber().formatE164(); // '+12125550198'Used in guides
Section titled “Used in guides”- Drive an input: wire
insert,deleteBackward,deleteForward, and the caret to DOM events. - Enforce strict mode: restrict resolution to one region.
- Load the engine: choose an entry point and gate controller creation on readiness.
See also
Section titled “See also”createNationalInputController: the fixed-region counterpart; the region is configured, not inferred.InputController: the full contract of the returned controller.- How the input controller works: the per-keystroke resolution mechanism.