ngxsmk-tel-input 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # ngxsmkTelInput
2
+
3
+ This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
8
+
9
+ ```bash
10
+ ng generate component component-name
11
+ ```
12
+
13
+ For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
+
15
+ ```bash
16
+ ng generate --help
17
+ ```
18
+
19
+ ## Building
20
+
21
+ To build the library, run:
22
+
23
+ ```bash
24
+ ng build ngxsmk-tel-input
25
+ ```
26
+
27
+ This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
28
+
29
+ ### Publishing the Library
30
+
31
+ Once the project is built, you can publish your library by following these steps:
32
+
33
+ 1. Navigate to the `dist` directory:
34
+ ```bash
35
+ cd dist/ngxsmk-tel-input
36
+ ```
37
+
38
+ 2. Run the `npm publish` command to publish your library to the npm registry:
39
+ ```bash
40
+ npm publish
41
+ ```
42
+
43
+ ## Running unit tests
44
+
45
+ To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
46
+
47
+ ```bash
48
+ ng test
49
+ ```
50
+
51
+ ## Running end-to-end tests
52
+
53
+ For end-to-end (e2e) testing, run:
54
+
55
+ ```bash
56
+ ng e2e
57
+ ```
58
+
59
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
60
+
61
+ ## Additional Resources
62
+
63
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
@@ -0,0 +1,377 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Optional, Inject, Injectable, EventEmitter, PLATFORM_ID, forwardRef, Output, Input, ViewChild, Component } from '@angular/core';
3
+ import { parsePhoneNumberFromString, AsYouType } from 'libphonenumber-js';
4
+ import { isPlatformBrowser } from '@angular/common';
5
+ import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
6
+
7
+ const ngxsmk_TEL_DEFAULTS = new InjectionToken('ngxsmk_TEL_DEFAULTS');
8
+ class ngxsmkTelInputService {
9
+ defaults = {
10
+ defaultCountry: 'US',
11
+ nationalMode: false
12
+ };
13
+ constructor(cfg) {
14
+ this.defaults = { ...this.defaults, ...(cfg ?? {}) };
15
+ }
16
+ /** Update defaults at runtime if you need to (multi-tenant apps, theming, etc.) */
17
+ setDefaults(partial) {
18
+ this.defaults = { ...this.defaults, ...partial };
19
+ }
20
+ getDefaults() {
21
+ return this.defaults;
22
+ }
23
+ /** Fast check: '+...' → true-ish shape (not full validation) */
24
+ looksLikeE164(v) {
25
+ return !!v && /^\+\d{3,}$/.test(v);
26
+ }
27
+ /**
28
+ * Parse any user input into structured data.
29
+ * - If input starts with +, region is inferred from the number.
30
+ * - Else uses provided `country` or the configured default.
31
+ */
32
+ parse(input, country) {
33
+ const raw = (input ?? '').trim();
34
+ if (!raw)
35
+ return { e164: null, isValid: false };
36
+ const region = this.looksLikeE164(raw) ? undefined : (country ?? this.defaults.defaultCountry);
37
+ const pn = parsePhoneNumberFromString(raw, region);
38
+ if (!pn)
39
+ return { e164: null, isValid: false };
40
+ const isValid = pn.isValid();
41
+ return {
42
+ e164: isValid ? pn.number : null,
43
+ national: pn.formatNational(),
44
+ international: pn.formatInternational(),
45
+ country: pn.country,
46
+ isValid,
47
+ raw: pn
48
+ };
49
+ }
50
+ /** Validate a number (raw user input or E.164). Optionally force region. */
51
+ isValid(input, country) {
52
+ return this.parse(input, country).isValid;
53
+ }
54
+ /** Format to E.164 (or null if invalid) */
55
+ toE164(input, country) {
56
+ return this.parse(input, country).e164;
57
+ }
58
+ /** Format nicely for display (international vs national) */
59
+ formatDisplay(input, opts) {
60
+ const { international = !this.defaults.nationalMode, country } = opts ?? {};
61
+ const p = this.parse(input, country);
62
+ if (!p.raw)
63
+ return input;
64
+ return international ? p.raw.formatInternational() : p.raw.formatNational();
65
+ }
66
+ /** As-you-type formatting for text inputs (pure function) */
67
+ asYouType(nextText, country) {
68
+ const region = country ?? this.defaults.defaultCountry;
69
+ const ayt = new AsYouType(region);
70
+ return ayt.input(nextText ?? '');
71
+ }
72
+ /** Infer country from E.164 or raw input (best effort) */
73
+ inferCountry(input) {
74
+ const p = this.parse(input);
75
+ return p.country;
76
+ }
77
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ngxsmkTelInputService, deps: [{ token: ngxsmk_TEL_DEFAULTS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
78
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ngxsmkTelInputService, providedIn: 'root' });
79
+ }
80
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ngxsmkTelInputService, decorators: [{
81
+ type: Injectable,
82
+ args: [{ providedIn: 'root' }]
83
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
84
+ type: Optional
85
+ }, {
86
+ type: Inject,
87
+ args: [ngxsmk_TEL_DEFAULTS]
88
+ }] }] });
89
+
90
+ class ngxsmkTelInputComponent {
91
+ zone;
92
+ tel;
93
+ platformId;
94
+ inputRef;
95
+ // ===== Inputs (public API) =====
96
+ /** Initial country (ISO2) or 'auto' to pick a default via geoIpLookup stub */
97
+ initialCountry = 'US';
98
+ /** Preferred countries on top of the dropdown */
99
+ preferredCountries = ['US', 'GB'];
100
+ /** Limit to these countries only (omit for all) */
101
+ onlyCountries;
102
+ /** Show national numbers instead of E.164 in the box (emits E.164) */
103
+ nationalMode = false;
104
+ /** Show the dial code separately before the input (intl-tel-input option) */
105
+ separateDialCode = false;
106
+ /** Allow opening the country dropdown */
107
+ allowDropdown = true;
108
+ /** Plain input attributes */
109
+ placeholder = 'Enter phone number';
110
+ autocomplete = 'tel';
111
+ name;
112
+ inputId;
113
+ /** Disabled state (also settable via Angular Forms) */
114
+ disabled = false;
115
+ // ===== Outputs =====
116
+ countryChange = new EventEmitter();
117
+ validityChange = new EventEmitter();
118
+ // ===== Internal =====
119
+ iti = null;
120
+ onChange = () => {
121
+ };
122
+ onTouched = () => {
123
+ };
124
+ lastEmittedValid = false;
125
+ pendingWrite = null; // cache writeValue before plugin ready
126
+ constructor(zone, tel, platformId) {
127
+ this.zone = zone;
128
+ this.tel = tel;
129
+ this.platformId = platformId;
130
+ }
131
+ // ========== Lifecycle ==========
132
+ async ngAfterViewInit() {
133
+ if (!isPlatformBrowser(this.platformId))
134
+ return;
135
+ await this.initIntlTelInput();
136
+ this.bindDomListeners();
137
+ // apply any pending value from writeValue
138
+ if (this.pendingWrite !== null) {
139
+ this.setInputValue(this.pendingWrite);
140
+ this.handleInput();
141
+ this.pendingWrite = null;
142
+ }
143
+ }
144
+ ngOnChanges(changes) {
145
+ if (!isPlatformBrowser(this.platformId))
146
+ return;
147
+ // If config inputs changed after init, re-init the plugin (safe & simple)
148
+ const configChanged = ['initialCountry', 'preferredCountries', 'onlyCountries', 'separateDialCode', 'allowDropdown', 'nationalMode']
149
+ .some(k => k in changes && !changes[k].firstChange);
150
+ if (configChanged && this.iti) {
151
+ this.reinitPlugin();
152
+ }
153
+ }
154
+ ngOnDestroy() {
155
+ this.destroyPlugin();
156
+ }
157
+ // ========== ControlValueAccessor ==========
158
+ writeValue(val) {
159
+ if (!this.inputRef)
160
+ return;
161
+ if (!this.iti) {
162
+ // cache until plugin is ready
163
+ this.pendingWrite = val ?? '';
164
+ return;
165
+ }
166
+ this.setInputValue(val ?? '');
167
+ // Do not trigger onChange here; writeValue is programmatic
168
+ }
169
+ registerOnChange(fn) {
170
+ this.onChange = fn;
171
+ }
172
+ registerOnTouched(fn) {
173
+ this.onTouched = fn;
174
+ }
175
+ setDisabledState(isDisabled) {
176
+ this.disabled = isDisabled;
177
+ if (this.inputRef)
178
+ this.inputRef.nativeElement.disabled = isDisabled;
179
+ }
180
+ // ========== Validator ==========
181
+ validate(_) {
182
+ const raw = this.currentRaw();
183
+ if (!raw)
184
+ return null; // let "required" handle empties
185
+ const valid = this.tel.isValid(raw, this.currentIso2());
186
+ if (valid !== this.lastEmittedValid) {
187
+ this.lastEmittedValid = valid;
188
+ this.validityChange.emit(valid);
189
+ }
190
+ return valid ? null : { phoneInvalid: true };
191
+ }
192
+ // ========== Public Helpers ==========
193
+ focus() {
194
+ this.inputRef?.nativeElement.focus();
195
+ }
196
+ selectCountry(iso2) {
197
+ if (this.iti) {
198
+ this.iti.setCountry(iso2.toLowerCase());
199
+ this.handleInput();
200
+ }
201
+ }
202
+ markTouched() {
203
+ this.onTouched();
204
+ }
205
+ // ========== Private: DOM & Plugin ==========
206
+ async initIntlTelInput() {
207
+ const [{ default: intlTelInput }] = await Promise.all([
208
+ import('intl-tel-input'),
209
+ ]);
210
+ // Minimal config – we rely on ngxsmkTelInputService for validation/formatting
211
+ const config = {
212
+ initialCountry: this.initialCountry === 'auto' ? 'auto' : (this.initialCountry?.toLowerCase() || 'us'),
213
+ preferredCountries: (this.preferredCountries ?? []).map(c => c.toLowerCase()),
214
+ onlyCountries: (this.onlyCountries ?? []).map(c => c.toLowerCase()),
215
+ nationalMode: this.nationalMode,
216
+ allowDropdown: this.allowDropdown,
217
+ separateDialCode: this.separateDialCode,
218
+ // If initialCountry is 'auto', provide a trivial geoIpLookup (customize in your app)
219
+ geoIpLookup: (cb) => cb('us'),
220
+ utilsScript: undefined // keep bundle small; we use libphonenumber-js via the service
221
+ };
222
+ this.zone.runOutsideAngular(() => {
223
+ this.iti = intlTelInput(this.inputRef.nativeElement, config);
224
+ });
225
+ }
226
+ reinitPlugin() {
227
+ this.destroyPlugin();
228
+ // keep current value
229
+ const current = this.currentRaw();
230
+ this.initIntlTelInput().then(() => {
231
+ if (current) {
232
+ this.setInputValue(current);
233
+ this.handleInput();
234
+ }
235
+ });
236
+ }
237
+ destroyPlugin() {
238
+ if (this.iti) {
239
+ this.iti.destroy();
240
+ this.iti = null;
241
+ }
242
+ // remove listeners by cloning node (simple & safe)
243
+ if (this.inputRef?.nativeElement) {
244
+ const el = this.inputRef.nativeElement;
245
+ const clone = el.cloneNode(true);
246
+ el.parentNode?.replaceChild(clone, el);
247
+ // update reference
248
+ this.inputRef.nativeElement = clone;
249
+ }
250
+ }
251
+ bindDomListeners() {
252
+ const el = this.inputRef.nativeElement;
253
+ this.zone.runOutsideAngular(() => {
254
+ el.addEventListener('input', () => this.handleInput());
255
+ el.addEventListener('countrychange', () => {
256
+ const iso2 = this.currentIso2();
257
+ this.zone.run(() => this.countryChange.emit({ iso2 }));
258
+ this.handleInput();
259
+ });
260
+ el.addEventListener('paste', () => queueMicrotask(() => this.handleInput()));
261
+ el.addEventListener('blur', () => this.zone.run(() => this.onTouched()));
262
+ });
263
+ }
264
+ handleInput() {
265
+ const raw = this.currentRaw();
266
+ const iso2 = this.currentIso2();
267
+ const parsed = this.tel.parse(raw, iso2);
268
+ // Emit E.164 (or null if invalid)
269
+ this.zone.run(() => this.onChange(parsed.e164));
270
+ // Optional: present national vs. international in the box without fighting the user
271
+ // We only normalize whitespace; avoid aggressive reformatting to preserve caret.
272
+ if (raw && this.nationalMode && parsed.national) {
273
+ // Replace double spaces etc. (intl-tel-input already styles)
274
+ const normalized = parsed.national.replace(/\s{2,}/g, ' ');
275
+ if (normalized !== raw)
276
+ this.setInputValue(normalized);
277
+ }
278
+ }
279
+ currentRaw() {
280
+ return (this.inputRef?.nativeElement.value ?? '').trim();
281
+ }
282
+ currentIso2() {
283
+ const iso2 = (this.iti?.getSelectedCountryData?.().iso2 ?? this.initialCountry ?? 'US').toString().toUpperCase();
284
+ return iso2;
285
+ }
286
+ setInputValue(v) {
287
+ this.inputRef.nativeElement.value = v ?? '';
288
+ }
289
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ngxsmkTelInputComponent, deps: [{ token: i0.NgZone }, { token: ngxsmkTelInputService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
290
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ngxsmkTelInputComponent, isStandalone: true, selector: "ngxsmk-tel-input", inputs: { initialCountry: "initialCountry", preferredCountries: "preferredCountries", onlyCountries: "onlyCountries", nationalMode: "nationalMode", separateDialCode: "separateDialCode", allowDropdown: "allowDropdown", placeholder: "placeholder", autocomplete: "autocomplete", name: "name", inputId: "inputId", disabled: "disabled" }, outputs: { countryChange: "countryChange", validityChange: "validityChange" }, providers: [
291
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ngxsmkTelInputComponent), multi: true },
292
+ { provide: NG_VALIDATORS, useExisting: forwardRef(() => ngxsmkTelInputComponent), multi: true }
293
+ ], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["telInput"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
294
+ <div class="ngxsmk-tel-input__wrapper">
295
+ <input
296
+ #telInput
297
+ type="tel"
298
+ class="ngxsmk-tel-input__control"
299
+ [id]="inputId || null"
300
+ [attr.name]="name || null"
301
+ [attr.placeholder]="placeholder || null"
302
+ [attr.autocomplete]="autocomplete"
303
+ [disabled]="disabled"
304
+ (blur)="markTouched()"
305
+ />
306
+ </div>
307
+ `, isInline: true });
308
+ }
309
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ngxsmkTelInputComponent, decorators: [{
310
+ type: Component,
311
+ args: [{
312
+ selector: 'ngxsmk-tel-input',
313
+ standalone: true,
314
+ template: `
315
+ <div class="ngxsmk-tel-input__wrapper">
316
+ <input
317
+ #telInput
318
+ type="tel"
319
+ class="ngxsmk-tel-input__control"
320
+ [id]="inputId || null"
321
+ [attr.name]="name || null"
322
+ [attr.placeholder]="placeholder || null"
323
+ [attr.autocomplete]="autocomplete"
324
+ [disabled]="disabled"
325
+ (blur)="markTouched()"
326
+ />
327
+ </div>
328
+ `,
329
+ providers: [
330
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ngxsmkTelInputComponent), multi: true },
331
+ { provide: NG_VALIDATORS, useExisting: forwardRef(() => ngxsmkTelInputComponent), multi: true }
332
+ ],
333
+ }]
334
+ }], ctorParameters: () => [{ type: i0.NgZone }, { type: ngxsmkTelInputService }, { type: Object, decorators: [{
335
+ type: Inject,
336
+ args: [PLATFORM_ID]
337
+ }] }], propDecorators: { inputRef: [{
338
+ type: ViewChild,
339
+ args: ['telInput', { static: true }]
340
+ }], initialCountry: [{
341
+ type: Input
342
+ }], preferredCountries: [{
343
+ type: Input
344
+ }], onlyCountries: [{
345
+ type: Input
346
+ }], nationalMode: [{
347
+ type: Input
348
+ }], separateDialCode: [{
349
+ type: Input
350
+ }], allowDropdown: [{
351
+ type: Input
352
+ }], placeholder: [{
353
+ type: Input
354
+ }], autocomplete: [{
355
+ type: Input
356
+ }], name: [{
357
+ type: Input
358
+ }], inputId: [{
359
+ type: Input
360
+ }], disabled: [{
361
+ type: Input
362
+ }], countryChange: [{
363
+ type: Output
364
+ }], validityChange: [{
365
+ type: Output
366
+ }] } });
367
+
368
+ /*
369
+ * Public API Surface of ngxsmk-tel-input
370
+ */
371
+
372
+ /**
373
+ * Generated bundle index. Do not edit.
374
+ */
375
+
376
+ export { ngxsmkTelInputComponent, ngxsmkTelInputService, ngxsmk_TEL_DEFAULTS };
377
+ //# sourceMappingURL=ngxsmk-tel-input.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngxsmk-tel-input.mjs","sources":["../../../projects/ngxsmk-tel-input/src/lib/ngxsmk-tel-input.service.ts","../../../projects/ngxsmk-tel-input/src/lib/ngxsmk-tel-input.component.ts","../../../projects/ngxsmk-tel-input/src/public-api.ts","../../../projects/ngxsmk-tel-input/src/ngxsmk-tel-input.ts"],"sourcesContent":["import { Inject, Injectable, InjectionToken, Optional } from '@angular/core';\r\nimport {\r\n AsYouType,\r\n CountryCode,\r\n parsePhoneNumberFromString,\r\n PhoneNumber\r\n} from 'libphonenumber-js';\r\n\r\nexport type E164 = `+${string}`;\r\n\r\nexport interface ngxsmkTelDefaults {\r\n /** Default country used when input is not in international form */\r\n defaultCountry?: CountryCode;\r\n /** If true, treat input/formatting as national by default */\r\n nationalMode?: boolean;\r\n}\r\n\r\nexport const ngxsmk_TEL_DEFAULTS = new InjectionToken<ngxsmkTelDefaults>('ngxsmk_TEL_DEFAULTS');\r\n\r\n/** Result of parsing a phone input */\r\nexport interface ParsedPhone {\r\n /** E.164 (+123...) if valid/parsable, else null */\r\n e164: E164 | null;\r\n /** National-formatted number (e.g., (415) 555-0123) */\r\n national?: string;\r\n /** International formatted number (e.g., +1 415 555 0123) */\r\n international?: string;\r\n /** 2-letter ISO country inferred by parser, if any */\r\n country?: CountryCode;\r\n /** Whether the number is valid for the country/region */\r\n isValid: boolean;\r\n /** Raw libphonenumber-js instance (optional) */\r\n raw?: PhoneNumber;\r\n}\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class ngxsmkTelInputService {\r\n private defaults: Required<ngxsmkTelDefaults> = {\r\n defaultCountry: 'US',\r\n nationalMode: false\r\n };\r\n\r\n constructor(@Optional() @Inject(ngxsmk_TEL_DEFAULTS) cfg?: ngxsmkTelDefaults) {\r\n this.defaults = { ...this.defaults, ...(cfg ?? {}) };\r\n }\r\n\r\n /** Update defaults at runtime if you need to (multi-tenant apps, theming, etc.) */\r\n setDefaults(partial: ngxsmkTelDefaults) {\r\n this.defaults = { ...this.defaults, ...partial };\r\n }\r\n\r\n getDefaults(): Readonly<Required<ngxsmkTelDefaults>> {\r\n return this.defaults;\r\n }\r\n\r\n /** Fast check: '+...' → true-ish shape (not full validation) */\r\n looksLikeE164(v?: string | null): v is E164 {\r\n return !!v && /^\\+\\d{3,}$/.test(v);\r\n }\r\n\r\n /**\r\n * Parse any user input into structured data.\r\n * - If input starts with +, region is inferred from the number.\r\n * - Else uses provided `country` or the configured default.\r\n */\r\n parse(input: string | null | undefined, country?: CountryCode): ParsedPhone {\r\n const raw = (input ?? '').trim();\r\n if (!raw) return { e164: null, isValid: false };\r\n\r\n const region = this.looksLikeE164(raw) ? undefined : (country ?? this.defaults.defaultCountry);\r\n const pn = parsePhoneNumberFromString(raw, region);\r\n\r\n if (!pn) return { e164: null, isValid: false };\r\n\r\n const isValid = pn.isValid();\r\n return {\r\n e164: isValid ? (pn.number as E164) : null,\r\n national: pn.formatNational(),\r\n international: pn.formatInternational(),\r\n country: pn.country as CountryCode | undefined,\r\n isValid,\r\n raw: pn\r\n };\r\n }\r\n\r\n /** Validate a number (raw user input or E.164). Optionally force region. */\r\n isValid(input: string, country?: CountryCode): boolean {\r\n return this.parse(input, country).isValid;\r\n }\r\n\r\n /** Format to E.164 (or null if invalid) */\r\n toE164(input: string, country?: CountryCode): E164 | null {\r\n return this.parse(input, country).e164;\r\n }\r\n\r\n /** Format nicely for display (international vs national) */\r\n formatDisplay(input: string, opts?: { international?: boolean; country?: CountryCode }): string {\r\n const { international = !this.defaults.nationalMode, country } = opts ?? {};\r\n const p = this.parse(input, country);\r\n if (!p.raw) return input;\r\n return international ? p.raw.formatInternational() : p.raw.formatNational();\r\n }\r\n\r\n /** As-you-type formatting for text inputs (pure function) */\r\n asYouType(nextText: string, country?: CountryCode): string {\r\n const region = country ?? this.defaults.defaultCountry;\r\n const ayt = new AsYouType(region);\r\n return ayt.input(nextText ?? '');\r\n }\r\n\r\n /** Infer country from E.164 or raw input (best effort) */\r\n inferCountry(input: string): CountryCode | undefined {\r\n const p = this.parse(input);\r\n return p.country;\r\n }\r\n}\r\n","import {\r\n AfterViewInit,\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n forwardRef,\r\n Inject,\r\n Input,\r\n NgZone,\r\n OnChanges,\r\n OnDestroy,\r\n Output,\r\n PLATFORM_ID,\r\n SimpleChanges,\r\n ViewChild\r\n} from '@angular/core';\r\nimport {isPlatformBrowser} from '@angular/common';\r\nimport {\r\n AbstractControl,\r\n ControlValueAccessor,\r\n NG_VALIDATORS,\r\n NG_VALUE_ACCESSOR,\r\n ValidationErrors\r\n} from '@angular/forms';\r\nimport type {CountryCode} from 'libphonenumber-js';\r\nimport {ngxsmkTelInputService} from './ngxsmk-tel-input.service';\r\n\r\ntype IntlTelInstance = any; // keep loose to avoid typing the plugin's full API\r\n\r\n@Component({\r\n selector: 'ngxsmk-tel-input',\r\n standalone: true,\r\n template: `\r\n <div class=\"ngxsmk-tel-input__wrapper\">\r\n <input\r\n #telInput\r\n type=\"tel\"\r\n class=\"ngxsmk-tel-input__control\"\r\n [id]=\"inputId || null\"\r\n [attr.name]=\"name || null\"\r\n [attr.placeholder]=\"placeholder || null\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [disabled]=\"disabled\"\r\n (blur)=\"markTouched()\"\r\n />\r\n </div>\r\n `,\r\n providers: [\r\n {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => ngxsmkTelInputComponent), multi: true},\r\n {provide: NG_VALIDATORS, useExisting: forwardRef(() => ngxsmkTelInputComponent), multi: true}\r\n ],\r\n})\r\nexport class ngxsmkTelInputComponent\r\n implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {\r\n @ViewChild('telInput', {static: true}) inputRef!: ElementRef<HTMLInputElement>;\r\n\r\n // ===== Inputs (public API) =====\r\n /** Initial country (ISO2) or 'auto' to pick a default via geoIpLookup stub */\r\n @Input() initialCountry: CountryCode | 'auto' = 'US';\r\n /** Preferred countries on top of the dropdown */\r\n @Input() preferredCountries: CountryCode[] = ['US', 'GB'];\r\n /** Limit to these countries only (omit for all) */\r\n @Input() onlyCountries?: CountryCode[];\r\n /** Show national numbers instead of E.164 in the box (emits E.164) */\r\n @Input() nationalMode = false;\r\n /** Show the dial code separately before the input (intl-tel-input option) */\r\n @Input() separateDialCode = false;\r\n /** Allow opening the country dropdown */\r\n @Input() allowDropdown = true;\r\n\r\n /** Plain input attributes */\r\n @Input() placeholder = 'Enter phone number';\r\n @Input() autocomplete = 'tel';\r\n @Input() name?: string;\r\n @Input() inputId?: string;\r\n\r\n /** Disabled state (also settable via Angular Forms) */\r\n @Input() disabled = false;\r\n\r\n // ===== Outputs =====\r\n @Output() countryChange = new EventEmitter<{ iso2: CountryCode }>();\r\n @Output() validityChange = new EventEmitter<boolean>();\r\n\r\n // ===== Internal =====\r\n private iti: IntlTelInstance | null = null;\r\n private onChange: (val: string | null) => void = () => {\r\n };\r\n private onTouched: () => void = () => {\r\n };\r\n private lastEmittedValid = false;\r\n private pendingWrite: string | null = null; // cache writeValue before plugin ready\r\n\r\n constructor(\r\n private readonly zone: NgZone,\r\n private readonly tel: ngxsmkTelInputService,\r\n @Inject(PLATFORM_ID) private readonly platformId: Object\r\n ) {\r\n }\r\n\r\n // ========== Lifecycle ==========\r\n async ngAfterViewInit() {\r\n if (!isPlatformBrowser(this.platformId)) return;\r\n\r\n await this.initIntlTelInput();\r\n this.bindDomListeners();\r\n\r\n // apply any pending value from writeValue\r\n if (this.pendingWrite !== null) {\r\n this.setInputValue(this.pendingWrite);\r\n this.handleInput();\r\n this.pendingWrite = null;\r\n }\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (!isPlatformBrowser(this.platformId)) return;\r\n\r\n // If config inputs changed after init, re-init the plugin (safe & simple)\r\n const configChanged = ['initialCountry', 'preferredCountries', 'onlyCountries', 'separateDialCode', 'allowDropdown', 'nationalMode']\r\n .some(k => k in changes && !changes[k].firstChange);\r\n\r\n if (configChanged && this.iti) {\r\n this.reinitPlugin();\r\n }\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.destroyPlugin();\r\n }\r\n\r\n // ========== ControlValueAccessor ==========\r\n writeValue(val: string | null): void {\r\n if (!this.inputRef) return;\r\n if (!this.iti) {\r\n // cache until plugin is ready\r\n this.pendingWrite = val ?? '';\r\n return;\r\n }\r\n this.setInputValue(val ?? '');\r\n // Do not trigger onChange here; writeValue is programmatic\r\n }\r\n\r\n registerOnChange(fn: any): void {\r\n this.onChange = fn;\r\n }\r\n\r\n registerOnTouched(fn: any): void {\r\n this.onTouched = fn;\r\n }\r\n\r\n setDisabledState(isDisabled: boolean): void {\r\n this.disabled = isDisabled;\r\n if (this.inputRef) this.inputRef.nativeElement.disabled = isDisabled;\r\n }\r\n\r\n // ========== Validator ==========\r\n validate(_: AbstractControl): ValidationErrors | null {\r\n const raw = this.currentRaw();\r\n if (!raw) return null; // let \"required\" handle empties\r\n const valid = this.tel.isValid(raw, this.currentIso2());\r\n if (valid !== this.lastEmittedValid) {\r\n this.lastEmittedValid = valid;\r\n this.validityChange.emit(valid);\r\n }\r\n return valid ? null : {phoneInvalid: true};\r\n }\r\n\r\n // ========== Public Helpers ==========\r\n focus(): void {\r\n this.inputRef?.nativeElement.focus();\r\n }\r\n\r\n selectCountry(iso2: CountryCode): void {\r\n if (this.iti) {\r\n this.iti.setCountry(iso2.toLowerCase());\r\n this.handleInput();\r\n }\r\n }\r\n\r\n markTouched() {\r\n this.onTouched();\r\n }\r\n\r\n // ========== Private: DOM & Plugin ==========\r\n private async initIntlTelInput() {\r\n const [{default: intlTelInput}] = await Promise.all([\r\n import('intl-tel-input'),\r\n ]);\r\n\r\n // Minimal config – we rely on ngxsmkTelInputService for validation/formatting\r\n const config: any = {\r\n initialCountry: this.initialCountry === 'auto' ? 'auto' : (this.initialCountry?.toLowerCase() || 'us'),\r\n preferredCountries: (this.preferredCountries ?? []).map(c => c.toLowerCase()),\r\n onlyCountries: (this.onlyCountries ?? []).map(c => c.toLowerCase()),\r\n nationalMode: this.nationalMode,\r\n allowDropdown: this.allowDropdown,\r\n separateDialCode: this.separateDialCode,\r\n // If initialCountry is 'auto', provide a trivial geoIpLookup (customize in your app)\r\n geoIpLookup: (cb: (iso2: string) => void) => cb('us'),\r\n utilsScript: undefined // keep bundle small; we use libphonenumber-js via the service\r\n };\r\n\r\n this.zone.runOutsideAngular(() => {\r\n this.iti = intlTelInput(this.inputRef.nativeElement, config);\r\n });\r\n }\r\n\r\n private reinitPlugin() {\r\n this.destroyPlugin();\r\n // keep current value\r\n const current = this.currentRaw();\r\n this.initIntlTelInput().then(() => {\r\n if (current) {\r\n this.setInputValue(current);\r\n this.handleInput();\r\n }\r\n });\r\n }\r\n\r\n private destroyPlugin() {\r\n if (this.iti) {\r\n this.iti.destroy();\r\n this.iti = null;\r\n }\r\n // remove listeners by cloning node (simple & safe)\r\n if (this.inputRef?.nativeElement) {\r\n const el = this.inputRef.nativeElement;\r\n const clone = el.cloneNode(true) as HTMLInputElement;\r\n el.parentNode?.replaceChild(clone, el);\r\n // update reference\r\n (this.inputRef as any).nativeElement = clone;\r\n }\r\n }\r\n\r\n private bindDomListeners() {\r\n const el = this.inputRef.nativeElement;\r\n\r\n this.zone.runOutsideAngular(() => {\r\n el.addEventListener('input', () => this.handleInput());\r\n el.addEventListener('countrychange', () => {\r\n const iso2 = this.currentIso2();\r\n this.zone.run(() => this.countryChange.emit({iso2}));\r\n this.handleInput();\r\n });\r\n el.addEventListener('paste', () => queueMicrotask(() => this.handleInput()));\r\n el.addEventListener('blur', () => this.zone.run(() => this.onTouched()));\r\n });\r\n }\r\n\r\n private handleInput() {\r\n const raw = this.currentRaw();\r\n const iso2 = this.currentIso2();\r\n\r\n const parsed = this.tel.parse(raw, iso2);\r\n // Emit E.164 (or null if invalid)\r\n this.zone.run(() => this.onChange(parsed.e164));\r\n\r\n // Optional: present national vs. international in the box without fighting the user\r\n // We only normalize whitespace; avoid aggressive reformatting to preserve caret.\r\n if (raw && this.nationalMode && parsed.national) {\r\n // Replace double spaces etc. (intl-tel-input already styles)\r\n const normalized = parsed.national.replace(/\\s{2,}/g, ' ');\r\n if (normalized !== raw) this.setInputValue(normalized);\r\n }\r\n }\r\n\r\n private currentRaw(): string {\r\n return (this.inputRef?.nativeElement.value ?? '').trim();\r\n }\r\n\r\n private currentIso2(): CountryCode {\r\n const iso2 = (this.iti?.getSelectedCountryData?.().iso2 ?? this.initialCountry ?? 'US').toString().toUpperCase();\r\n return iso2 as CountryCode;\r\n }\r\n\r\n private setInputValue(v: string) {\r\n this.inputRef.nativeElement.value = v ?? '';\r\n }\r\n}\r\n","/*\r\n * Public API Surface of ngxsmk-tel-input\r\n */\r\n\r\nexport * from './lib/ngxsmk-tel-input.service';\r\nexport * from './lib/ngxsmk-tel-input.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ /// <amd-module name="ngxsmk-tel-input" />
5
+ export * from './public-api';
@@ -0,0 +1,61 @@
1
+ import { AfterViewInit, ElementRef, EventEmitter, NgZone, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
2
+ import { AbstractControl, ControlValueAccessor, ValidationErrors } from '@angular/forms';
3
+ import type { CountryCode } from 'libphonenumber-js';
4
+ import { ngxsmkTelInputService } from './ngxsmk-tel-input.service';
5
+ import * as i0 from "@angular/core";
6
+ export declare class ngxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {
7
+ private readonly zone;
8
+ private readonly tel;
9
+ private readonly platformId;
10
+ inputRef: ElementRef<HTMLInputElement>;
11
+ /** Initial country (ISO2) or 'auto' to pick a default via geoIpLookup stub */
12
+ initialCountry: CountryCode | 'auto';
13
+ /** Preferred countries on top of the dropdown */
14
+ preferredCountries: CountryCode[];
15
+ /** Limit to these countries only (omit for all) */
16
+ onlyCountries?: CountryCode[];
17
+ /** Show national numbers instead of E.164 in the box (emits E.164) */
18
+ nationalMode: boolean;
19
+ /** Show the dial code separately before the input (intl-tel-input option) */
20
+ separateDialCode: boolean;
21
+ /** Allow opening the country dropdown */
22
+ allowDropdown: boolean;
23
+ /** Plain input attributes */
24
+ placeholder: string;
25
+ autocomplete: string;
26
+ name?: string;
27
+ inputId?: string;
28
+ /** Disabled state (also settable via Angular Forms) */
29
+ disabled: boolean;
30
+ countryChange: EventEmitter<{
31
+ iso2: CountryCode;
32
+ }>;
33
+ validityChange: EventEmitter<boolean>;
34
+ private iti;
35
+ private onChange;
36
+ private onTouched;
37
+ private lastEmittedValid;
38
+ private pendingWrite;
39
+ constructor(zone: NgZone, tel: ngxsmkTelInputService, platformId: Object);
40
+ ngAfterViewInit(): Promise<void>;
41
+ ngOnChanges(changes: SimpleChanges): void;
42
+ ngOnDestroy(): void;
43
+ writeValue(val: string | null): void;
44
+ registerOnChange(fn: any): void;
45
+ registerOnTouched(fn: any): void;
46
+ setDisabledState(isDisabled: boolean): void;
47
+ validate(_: AbstractControl): ValidationErrors | null;
48
+ focus(): void;
49
+ selectCountry(iso2: CountryCode): void;
50
+ markTouched(): void;
51
+ private initIntlTelInput;
52
+ private reinitPlugin;
53
+ private destroyPlugin;
54
+ private bindDomListeners;
55
+ private handleInput;
56
+ private currentRaw;
57
+ private currentIso2;
58
+ private setInputValue;
59
+ static ɵfac: i0.ɵɵFactoryDeclaration<ngxsmkTelInputComponent, never>;
60
+ static ɵcmp: i0.ɵɵComponentDeclaration<ngxsmkTelInputComponent, "ngxsmk-tel-input", never, { "initialCountry": { "alias": "initialCountry"; "required": false; }; "preferredCountries": { "alias": "preferredCountries"; "required": false; }; "onlyCountries": { "alias": "onlyCountries"; "required": false; }; "nationalMode": { "alias": "nationalMode"; "required": false; }; "separateDialCode": { "alias": "separateDialCode"; "required": false; }; "allowDropdown": { "alias": "allowDropdown"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "autocomplete": { "alias": "autocomplete"; "required": false; }; "name": { "alias": "name"; "required": false; }; "inputId": { "alias": "inputId"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; }, { "countryChange": "countryChange"; "validityChange": "validityChange"; }, never, never, true, never>;
61
+ }
@@ -0,0 +1,56 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ import { CountryCode, PhoneNumber } from 'libphonenumber-js';
3
+ import * as i0 from "@angular/core";
4
+ export type E164 = `+${string}`;
5
+ export interface ngxsmkTelDefaults {
6
+ /** Default country used when input is not in international form */
7
+ defaultCountry?: CountryCode;
8
+ /** If true, treat input/formatting as national by default */
9
+ nationalMode?: boolean;
10
+ }
11
+ export declare const ngxsmk_TEL_DEFAULTS: InjectionToken<ngxsmkTelDefaults>;
12
+ /** Result of parsing a phone input */
13
+ export interface ParsedPhone {
14
+ /** E.164 (+123...) if valid/parsable, else null */
15
+ e164: E164 | null;
16
+ /** National-formatted number (e.g., (415) 555-0123) */
17
+ national?: string;
18
+ /** International formatted number (e.g., +1 415 555 0123) */
19
+ international?: string;
20
+ /** 2-letter ISO country inferred by parser, if any */
21
+ country?: CountryCode;
22
+ /** Whether the number is valid for the country/region */
23
+ isValid: boolean;
24
+ /** Raw libphonenumber-js instance (optional) */
25
+ raw?: PhoneNumber;
26
+ }
27
+ export declare class ngxsmkTelInputService {
28
+ private defaults;
29
+ constructor(cfg?: ngxsmkTelDefaults);
30
+ /** Update defaults at runtime if you need to (multi-tenant apps, theming, etc.) */
31
+ setDefaults(partial: ngxsmkTelDefaults): void;
32
+ getDefaults(): Readonly<Required<ngxsmkTelDefaults>>;
33
+ /** Fast check: '+...' → true-ish shape (not full validation) */
34
+ looksLikeE164(v?: string | null): v is E164;
35
+ /**
36
+ * Parse any user input into structured data.
37
+ * - If input starts with +, region is inferred from the number.
38
+ * - Else uses provided `country` or the configured default.
39
+ */
40
+ parse(input: string | null | undefined, country?: CountryCode): ParsedPhone;
41
+ /** Validate a number (raw user input or E.164). Optionally force region. */
42
+ isValid(input: string, country?: CountryCode): boolean;
43
+ /** Format to E.164 (or null if invalid) */
44
+ toE164(input: string, country?: CountryCode): E164 | null;
45
+ /** Format nicely for display (international vs national) */
46
+ formatDisplay(input: string, opts?: {
47
+ international?: boolean;
48
+ country?: CountryCode;
49
+ }): string;
50
+ /** As-you-type formatting for text inputs (pure function) */
51
+ asYouType(nextText: string, country?: CountryCode): string;
52
+ /** Infer country from E.164 or raw input (best effort) */
53
+ inferCountry(input: string): CountryCode | undefined;
54
+ static ɵfac: i0.ɵɵFactoryDeclaration<ngxsmkTelInputService, [{ optional: true; }]>;
55
+ static ɵprov: i0.ɵɵInjectableDeclaration<ngxsmkTelInputService>;
56
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "ngxsmk-tel-input",
3
+ "version": "0.0.1",
4
+ "peerDependencies": {
5
+ "@angular/common": "^19.2.0",
6
+ "@angular/core": "^19.2.0"
7
+ },
8
+ "dependencies": {
9
+ "tslib": "^2.3.0"
10
+ },
11
+ "sideEffects": false,
12
+ "module": "fesm2022/ngxsmk-tel-input.mjs",
13
+ "typings": "index.d.ts",
14
+ "exports": {
15
+ "./package.json": {
16
+ "default": "./package.json"
17
+ },
18
+ ".": {
19
+ "types": "./index.d.ts",
20
+ "default": "./fesm2022/ngxsmk-tel-input.mjs"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,2 @@
1
+ export * from './lib/ngxsmk-tel-input.service';
2
+ export * from './lib/ngxsmk-tel-input.component';