or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

as-you-type-formatting.mdcore-parsing-formatting.mdindex.mdnumber-validation.mdphone-number-matching.mdregion-metadata.mdshort-numbers.mdutility-functions.md

as-you-type-formatting.mddocs/

0

# As-You-Type Formatting

1

2

Interactive phone number formatting for user interfaces, providing real-time formatting as users type phone numbers. This capability enables better user experience in forms and input fields.

3

4

## Capabilities

5

6

### AsYouTypeFormatter Class

7

8

The main class for providing real-time phone number formatting as users enter digits.

9

10

```python { .api }

11

class AsYouTypeFormatter:

12

"""

13

A formatter that formats phone numbers as they are being typed.

14

15

Provides real-time formatting feedback for user interfaces,

16

automatically applying appropriate formatting patterns based

17

on the digits entered so far.

18

"""

19

20

def __init__(self, region_code: str):

21

"""

22

Initialize formatter for a specific region.

23

24

Parameters:

25

- region_code: Two-letter region code for formatting context

26

"""

27

28

def input_digit(self, next_char: str, remember_position: bool = False) -> str:

29

"""

30

Input a single digit and get the formatted result.

31

32

Parameters:

33

- next_char: Single character ('0'-'9', '+', etc.) to add

34

- remember_position: Whether to remember position for cursor tracking

35

36

Returns:

37

Formatted phone number string with the new character incorporated

38

"""

39

40

def clear(self) -> None:

41

"""

42

Clear the formatter state and prepare for new input.

43

44

Clears internal state so the formatter can be reused.

45

"""

46

47

def get_remembered_position(self) -> int:

48

"""

49

Get the position in formatted output of a remembered character.

50

51

Returns position of character that was input with remember_position=True.

52

53

Returns:

54

Position index in the formatted string

55

"""

56

```

57

58

## Usage Examples

59

60

### Basic As-You-Type Formatting

61

62

```python

63

import phonenumbers

64

65

# Create formatter for US numbers

66

formatter = phonenumbers.AsYouTypeFormatter("US")

67

68

# Simulate user typing a phone number

69

digits = "6502532222"

70

print("As user types:")

71

72

for digit in digits:

73

result = formatter.input_digit(digit)

74

print(f" Input '{digit}' -> '{result}'")

75

76

# Output will show progressive formatting:

77

# Input '6' -> '6'

78

# Input '5' -> '65'

79

# Input '0' -> '650'

80

# Input '2' -> '650-2'

81

# Input '5' -> '650-25'

82

# Input '3' -> '650-253'

83

# Input '2' -> '650-2532'

84

# Input '2' -> '(650) 253-22'

85

# Input '2' -> '(650) 253-222'

86

# Input '2' -> '(650) 253-2222'

87

```

88

89

### International Number Formatting

90

91

```python

92

import phonenumbers

93

94

# Format international number as user types

95

formatter = phonenumbers.AsYouTypeFormatter("US")

96

97

# User starts with international prefix

98

international_digits = "+442083661177"

99

print("International number formatting:")

100

101

for digit in international_digits:

102

result = formatter.input_digit(digit)

103

print(f" '{digit}' -> '{result}'")

104

105

# Shows progression from local to international formatting

106

```

107

108

### Interactive Phone Input Implementation

109

110

```python

111

import phonenumbers

112

113

class PhoneInputHandler:

114

"""Example implementation of interactive phone number input."""

115

116

def __init__(self, default_region="US"):

117

self.formatter = phonenumbers.AsYouTypeFormatter(default_region)

118

self.current_value = ""

119

120

def handle_digit_input(self, digit):

121

"""Handle single digit input from user."""

122

if digit.isdigit() or digit in "+":

123

formatted = self.formatter.input_digit(digit)

124

self.current_value = formatted

125

return formatted

126

return self.current_value

127

128

def handle_backspace(self):

129

"""Handle backspace/delete key."""

130

if self.current_value:

131

# Clear formatter and re-input all but last character

132

self.formatter.clear()

133

input_chars = [c for c in self.current_value if c.isdigit() or c == '+']

134

if input_chars:

135

input_chars.pop() # Remove last character

136

137

self.current_value = ""

138

for char in input_chars:

139

self.current_value = self.formatter.input_digit(char)

140

return self.current_value

141

return ""

142

143

def clear_input(self):

144

"""Clear all input."""

145

self.formatter.clear()

146

self.current_value = ""

147

return ""

148

149

def get_parsed_number(self):

150

"""Get parsed PhoneNumber object if input is valid."""

151

try:

152

# Remove formatting for parsing

153

clean_number = ''.join(filter(str.isdigit, self.current_value))

154

if self.current_value.startswith('+'):

155

clean_number = '+' + clean_number

156

157

return phonenumbers.parse(clean_number)

158

except phonenumbers.NumberParseException:

159

return None

160

161

def is_valid_number(self):

162

"""Check if current input represents a valid number."""

163

parsed = self.get_parsed_number()

164

if parsed:

165

return phonenumbers.is_valid_number(parsed)

166

return False

167

168

# Example usage

169

phone_input = PhoneInputHandler("US")

170

171

# Simulate user input

172

test_input = "6502532222"

173

print("Simulating phone input:")

174

175

for digit in test_input:

176

formatted = phone_input.handle_digit_input(digit)

177

is_valid = phone_input.is_valid_number()

178

print(f"Input: '{digit}' -> Display: '{formatted}' (Valid: {is_valid})")

179

180

# Test backspace

181

print("\nTesting backspace:")

182

for _ in range(3):

183

formatted = phone_input.handle_backspace()

184

is_valid = phone_input.is_valid_number()

185

print(f"After backspace: '{formatted}' (Valid: {is_valid})")

186

```

187

188

### Multi-Region Support

189

190

```python

191

import phonenumbers

192

193

class MultiRegionPhoneFormatter:

194

"""Phone formatter that can switch regions based on input."""

195

196

def __init__(self, default_region="US"):

197

self.default_region = default_region

198

self.current_region = default_region

199

self.formatter = phonenumbers.AsYouTypeFormatter(default_region)

200

self.input_buffer = ""

201

202

def detect_region_from_input(self, input_so_far):

203

"""Attempt to detect region from country code in input."""

204

if input_so_far.startswith('+'):

205

# Try to parse and determine region

206

try:

207

# Extract potential country code

208

digits_only = ''.join(filter(str.isdigit, input_so_far))

209

if len(digits_only) >= 1:

210

# Try different country code lengths

211

for cc_length in [1, 2, 3]:

212

if len(digits_only) >= cc_length:

213

potential_cc = int(digits_only[:cc_length])

214

region = phonenumbers.region_code_for_country_code(potential_cc)

215

if region and region != "ZZ":

216

return region

217

except (ValueError, TypeError):

218

pass

219

return self.default_region

220

221

def input_digit(self, digit):

222

"""Input digit with automatic region detection."""

223

self.input_buffer += digit

224

225

# Check if we need to switch regions

226

detected_region = self.detect_region_from_input(self.input_buffer)

227

228

if detected_region != self.current_region:

229

# Switch to new region and restart formatting

230

self.current_region = detected_region

231

self.formatter = phonenumbers.AsYouTypeFormatter(detected_region)

232

233

# Re-input all digits with new formatter

234

result = ""

235

for d in self.input_buffer:

236

result = self.formatter.input_digit(d)

237

return result

238

else:

239

return self.formatter.input_digit(digit)

240

241

def clear(self):

242

"""Clear formatter and reset to default region."""

243

self.input_buffer = ""

244

self.current_region = self.default_region

245

self.formatter = phonenumbers.AsYouTypeFormatter(self.default_region)

246

self.formatter.clear()

247

return ""

248

249

# Example usage

250

multi_formatter = MultiRegionPhoneFormatter("US")

251

252

# Test with US number

253

us_number = "6502532222"

254

print("US number formatting:")

255

for digit in us_number:

256

result = multi_formatter.input_digit(digit)

257

print(f" '{digit}' -> '{result}' (Region: {multi_formatter.current_region})")

258

259

print()

260

multi_formatter.clear()

261

262

# Test with UK number (starts with +44)

263

uk_number = "+442083661177"

264

print("UK number formatting:")

265

for digit in uk_number:

266

result = multi_formatter.input_digit(digit)

267

print(f" '{digit}' -> '{result}' (Region: {multi_formatter.current_region})")

268

```

269

270

### Form Validation Integration

271

272

```python

273

import phonenumbers

274

275

class PhoneNumberField:

276

"""Phone number field with real-time validation and formatting."""

277

278

def __init__(self, region="US", required=True):

279

self.region = region

280

self.required = required

281

self.formatter = phonenumbers.AsYouTypeFormatter(region)

282

self.raw_value = ""

283

self.formatted_value = ""

284

self.is_valid = False

285

self.validation_message = ""

286

287

def set_value(self, value):

288

"""Set the field value and update formatting."""

289

self.raw_value = value

290

self.formatter.clear()

291

292

# Apply formatting character by character

293

self.formatted_value = ""

294

for char in value:

295

if char.isdigit() or char == '+':

296

self.formatted_value = self.formatter.input_digit(char)

297

298

# Validate the result

299

self._validate()

300

301

return {

302

'formatted': self.formatted_value,

303

'is_valid': self.is_valid,

304

'message': self.validation_message

305

}

306

307

def _validate(self):

308

"""Internal validation logic."""

309

if not self.raw_value and self.required:

310

self.is_valid = False

311

self.validation_message = "Phone number is required"

312

return

313

314

if not self.raw_value:

315

self.is_valid = True

316

self.validation_message = ""

317

return

318

319

try:

320

parsed = phonenumbers.parse(self.raw_value, self.region)

321

322

if phonenumbers.is_valid_number(parsed):

323

self.is_valid = True

324

self.validation_message = ""

325

elif phonenumbers.is_possible_number(parsed):

326

self.is_valid = False

327

self.validation_message = "Phone number format is not valid"

328

else:

329

reason = phonenumbers.is_possible_number_with_reason(parsed)

330

if reason == phonenumbers.ValidationResult.TOO_SHORT:

331

self.validation_message = "Phone number is too short"

332

elif reason == phonenumbers.ValidationResult.TOO_LONG:

333

self.validation_message = "Phone number is too long"

334

else:

335

self.validation_message = "Invalid phone number"

336

self.is_valid = False

337

338

except phonenumbers.NumberParseException as e:

339

self.is_valid = False

340

if e.error_type == phonenumbers.NumberParseException.INVALID_COUNTRY_CODE:

341

self.validation_message = "Invalid country code"

342

elif e.error_type == phonenumbers.NumberParseException.NOT_A_NUMBER:

343

self.validation_message = "Not a valid phone number"

344

else:

345

self.validation_message = "Invalid phone number format"

346

347

# Example usage

348

phone_field = PhoneNumberField("US", required=True)

349

350

# Test various inputs

351

test_inputs = [

352

"", # Empty (required field)

353

"650", # Too short

354

"6502532222", # Valid US number

355

"+442083661177", # Valid UK number

356

"invalid", # Not a number

357

"65025322221234567", # Too long

358

]

359

360

for test_input in test_inputs:

361

result = phone_field.set_value(test_input)

362

print(f"Input: '{test_input}'")

363

print(f" Formatted: '{result['formatted']}'")

364

print(f" Valid: {result['is_valid']}")

365

print(f" Message: '{result['message']}'")

366

print()

367

```

368

369

## Integration Patterns

370

371

### Web Framework Integration

372

373

```python

374

import phonenumbers

375

376

def create_phone_input_widget(name, region="US", placeholder=None):

377

"""Create HTML for phone input with JavaScript formatting."""

378

379

placeholder_text = placeholder or f"Enter {region} phone number"

380

381

html = f'''

382

<div class="phone-input-container">

383

<input type="tel"

384

name="{name}"

385

id="{name}"

386

placeholder="{placeholder_text}"

387

data-region="{region}"

388

class="phone-input">

389

<div class="validation-message" id="{name}-validation"></div>

390

</div>

391

392

<script>

393

// JavaScript integration would use phonenumbers library

394

// or server-side formatting via AJAX calls

395

document.getElementById('{name}').addEventListener('input', function(e) {{

396

// Format input in real-time

397

// Validate and show feedback

398

}});

399

</script>

400

'''

401

402

return html

403

404

# Server-side formatting endpoint

405

def format_phone_api(request):

406

"""API endpoint for real-time phone formatting."""

407

digit = request.GET.get('digit', '')

408

region = request.GET.get('region', 'US')

409

current_state = request.GET.get('state', '')

410

411

# In a real implementation, you'd maintain formatter state

412

# in session or pass it back and forth

413

414

formatter = phonenumbers.AsYouTypeFormatter(region)

415

416

# Restore previous state

417

for d in current_state:

418

if d.isdigit() or d == '+':

419

formatter.input_digit(d)

420

421

# Add new digit

422

if digit:

423

result = formatter.input_digit(digit)

424

return {'formatted': result, 'state': current_state + digit}

425

426

return {'formatted': current_state, 'state': current_state}

427

```