ngxsmk-tel-input 0.0.3 → 0.0.7
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.
|
@@ -1,187 +1,124 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { isPlatformBrowser } from '@angular/common';
|
|
2
|
+
import { Injectable, EventEmitter, PLATFORM_ID, forwardRef, Output, Input, ViewChild, Inject, Component } from '@angular/core';
|
|
3
|
+
import { isPlatformBrowser, NgIf } from '@angular/common';
|
|
5
4
|
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
|
|
5
|
+
import { parsePhoneNumberFromString } from 'libphonenumber-js';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
this.defaults = { ...this.defaults, ...(cfg ?? {}) };
|
|
7
|
+
class NgxsmkTelInputService {
|
|
8
|
+
parse(input, iso2) {
|
|
9
|
+
const phone = parsePhoneNumberFromString(input, iso2);
|
|
10
|
+
if (!phone)
|
|
11
|
+
return { e164: null, national: null, isValid: false };
|
|
12
|
+
const isValid = phone.isValid();
|
|
13
|
+
return { e164: isValid ? phone.number : null, national: phone.formatNational(), isValid };
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
isValid(input, iso2) {
|
|
16
|
+
const phone = parsePhoneNumberFromString(input, iso2);
|
|
17
|
+
return !!phone && phone.isValid();
|
|
19
18
|
}
|
|
20
|
-
|
|
21
|
-
|
|
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' });
|
|
19
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxsmkTelInputService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
20
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxsmkTelInputService, providedIn: 'root' });
|
|
79
21
|
}
|
|
80
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
22
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxsmkTelInputService, decorators: [{
|
|
81
23
|
type: Injectable,
|
|
82
24
|
args: [{ providedIn: 'root' }]
|
|
83
|
-
}]
|
|
84
|
-
type: Optional
|
|
85
|
-
}, {
|
|
86
|
-
type: Inject,
|
|
87
|
-
args: [ngxsmk_TEL_DEFAULTS]
|
|
88
|
-
}] }] });
|
|
25
|
+
}] });
|
|
89
26
|
|
|
90
|
-
class
|
|
27
|
+
class NgxsmkTelInputComponent {
|
|
91
28
|
zone;
|
|
92
29
|
tel;
|
|
93
30
|
platformId;
|
|
94
31
|
inputRef;
|
|
95
|
-
|
|
96
|
-
/** Initial country (ISO2) or 'auto' to pick a default via geoIpLookup stub */
|
|
32
|
+
/* Existing inputs */
|
|
97
33
|
initialCountry = 'US';
|
|
98
|
-
/** Preferred countries on top of the dropdown */
|
|
99
34
|
preferredCountries = ['US', 'GB'];
|
|
100
|
-
/** Limit to these countries only (omit for all) */
|
|
101
35
|
onlyCountries;
|
|
102
|
-
/** Show national numbers instead of E.164 in the box (emits E.164) */
|
|
103
36
|
nationalMode = false;
|
|
104
|
-
/** Show the dial code separately before the input (intl-tel-input option) */
|
|
105
37
|
separateDialCode = false;
|
|
106
|
-
/** Allow opening the country dropdown */
|
|
107
38
|
allowDropdown = true;
|
|
108
|
-
/** Plain input attributes */
|
|
109
39
|
placeholder = 'Enter phone number';
|
|
110
40
|
autocomplete = 'tel';
|
|
111
41
|
name;
|
|
112
42
|
inputId;
|
|
113
|
-
/** Disabled state (also settable via Angular Forms) */
|
|
114
43
|
disabled = false;
|
|
115
|
-
|
|
44
|
+
/* New UI/UX inputs */
|
|
45
|
+
label;
|
|
46
|
+
hint;
|
|
47
|
+
errorText;
|
|
48
|
+
size = 'md';
|
|
49
|
+
variant = 'outline';
|
|
50
|
+
showClear = true;
|
|
51
|
+
autoFocus = false;
|
|
52
|
+
selectOnFocus = false;
|
|
53
|
+
formatOnBlur = true;
|
|
54
|
+
showErrorWhenTouched = true;
|
|
55
|
+
/** Dropdown plumbing */
|
|
56
|
+
dropdownAttachToBody = true; // append dropdown to <body> (escapes overflow/clip)
|
|
57
|
+
dropdownZIndex = 2000; // used by CSS var --tel-dd-z
|
|
58
|
+
/* Outputs */
|
|
116
59
|
countryChange = new EventEmitter();
|
|
117
60
|
validityChange = new EventEmitter();
|
|
118
|
-
|
|
61
|
+
inputChange = new EventEmitter();
|
|
62
|
+
/* Internal */
|
|
119
63
|
iti = null;
|
|
120
|
-
onChange = () => {
|
|
121
|
-
};
|
|
122
|
-
onTouched = () => {
|
|
123
|
-
};
|
|
64
|
+
onChange = () => { };
|
|
65
|
+
onTouchedCb = () => { };
|
|
124
66
|
lastEmittedValid = false;
|
|
125
|
-
pendingWrite = null;
|
|
67
|
+
pendingWrite = null;
|
|
68
|
+
touched = false;
|
|
69
|
+
resolvedId = (() => 'tel-' + Math.random().toString(36).slice(2))();
|
|
126
70
|
constructor(zone, tel, platformId) {
|
|
127
71
|
this.zone = zone;
|
|
128
72
|
this.tel = tel;
|
|
129
73
|
this.platformId = platformId;
|
|
130
74
|
}
|
|
131
|
-
// ========== Lifecycle ==========
|
|
132
75
|
async ngAfterViewInit() {
|
|
133
76
|
if (!isPlatformBrowser(this.platformId))
|
|
134
77
|
return;
|
|
78
|
+
// set z-index via CSS var
|
|
79
|
+
this.constructor; // no-op to keep TS calm
|
|
80
|
+
this.inputRef.nativeElement.closest(':host');
|
|
135
81
|
await this.initIntlTelInput();
|
|
136
82
|
this.bindDomListeners();
|
|
137
|
-
// apply any pending value from writeValue
|
|
138
83
|
if (this.pendingWrite !== null) {
|
|
139
84
|
this.setInputValue(this.pendingWrite);
|
|
140
85
|
this.handleInput();
|
|
141
86
|
this.pendingWrite = null;
|
|
142
87
|
}
|
|
88
|
+
if (this.autoFocus)
|
|
89
|
+
setTimeout(() => this.focus(), 0);
|
|
143
90
|
}
|
|
144
91
|
ngOnChanges(changes) {
|
|
145
92
|
if (!isPlatformBrowser(this.platformId))
|
|
146
93
|
return;
|
|
147
|
-
// If config inputs changed after init, re-init the plugin (safe & simple)
|
|
148
94
|
const configChanged = ['initialCountry', 'preferredCountries', 'onlyCountries', 'separateDialCode', 'allowDropdown', 'nationalMode']
|
|
149
95
|
.some(k => k in changes && !changes[k].firstChange);
|
|
150
|
-
if (configChanged && this.iti)
|
|
96
|
+
if (configChanged && this.iti)
|
|
151
97
|
this.reinitPlugin();
|
|
152
|
-
}
|
|
153
98
|
}
|
|
154
|
-
ngOnDestroy() {
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
// ========== ControlValueAccessor ==========
|
|
99
|
+
ngOnDestroy() { this.destroyPlugin(); }
|
|
100
|
+
// ----- CVA -----
|
|
158
101
|
writeValue(val) {
|
|
159
102
|
if (!this.inputRef)
|
|
160
103
|
return;
|
|
161
104
|
if (!this.iti) {
|
|
162
|
-
// cache until plugin is ready
|
|
163
105
|
this.pendingWrite = val ?? '';
|
|
164
106
|
return;
|
|
165
107
|
}
|
|
166
108
|
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
109
|
}
|
|
110
|
+
registerOnChange(fn) { this.onChange = fn; }
|
|
111
|
+
registerOnTouched(fn) { this.onTouchedCb = fn; }
|
|
175
112
|
setDisabledState(isDisabled) {
|
|
176
113
|
this.disabled = isDisabled;
|
|
177
114
|
if (this.inputRef)
|
|
178
115
|
this.inputRef.nativeElement.disabled = isDisabled;
|
|
179
116
|
}
|
|
180
|
-
//
|
|
117
|
+
// ----- Validator -----
|
|
181
118
|
validate(_) {
|
|
182
119
|
const raw = this.currentRaw();
|
|
183
120
|
if (!raw)
|
|
184
|
-
return null;
|
|
121
|
+
return null;
|
|
185
122
|
const valid = this.tel.isValid(raw, this.currentIso2());
|
|
186
123
|
if (valid !== this.lastEmittedValid) {
|
|
187
124
|
this.lastEmittedValid = valid;
|
|
@@ -189,9 +126,13 @@ class ngxsmkTelInputComponent {
|
|
|
189
126
|
}
|
|
190
127
|
return valid ? null : { phoneInvalid: true };
|
|
191
128
|
}
|
|
192
|
-
//
|
|
129
|
+
// ----- Public helpers -----
|
|
193
130
|
focus() {
|
|
194
131
|
this.inputRef?.nativeElement.focus();
|
|
132
|
+
if (this.selectOnFocus) {
|
|
133
|
+
const el = this.inputRef.nativeElement;
|
|
134
|
+
queueMicrotask(() => el.setSelectionRange(0, el.value.length));
|
|
135
|
+
}
|
|
195
136
|
}
|
|
196
137
|
selectCountry(iso2) {
|
|
197
138
|
if (this.iti) {
|
|
@@ -199,15 +140,14 @@ class ngxsmkTelInputComponent {
|
|
|
199
140
|
this.handleInput();
|
|
200
141
|
}
|
|
201
142
|
}
|
|
202
|
-
|
|
203
|
-
this.
|
|
143
|
+
clearInput() {
|
|
144
|
+
this.setInputValue('');
|
|
145
|
+
this.handleInput();
|
|
146
|
+
this.inputRef.nativeElement.focus();
|
|
204
147
|
}
|
|
205
|
-
//
|
|
148
|
+
// ----- Plugin wiring -----
|
|
206
149
|
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
|
|
150
|
+
const [{ default: intlTelInput }] = await Promise.all([import('intl-tel-input')]);
|
|
211
151
|
const config = {
|
|
212
152
|
initialCountry: this.initialCountry === 'auto' ? 'auto' : (this.initialCountry?.toLowerCase() || 'us'),
|
|
213
153
|
preferredCountries: (this.preferredCountries ?? []).map(c => c.toLowerCase()),
|
|
@@ -215,18 +155,17 @@ class ngxsmkTelInputComponent {
|
|
|
215
155
|
nationalMode: this.nationalMode,
|
|
216
156
|
allowDropdown: this.allowDropdown,
|
|
217
157
|
separateDialCode: this.separateDialCode,
|
|
218
|
-
// If initialCountry is 'auto', provide a trivial geoIpLookup (customize in your app)
|
|
219
158
|
geoIpLookup: (cb) => cb('us'),
|
|
220
|
-
utilsScript: undefined
|
|
159
|
+
utilsScript: undefined,
|
|
160
|
+
dropdownContainer: this.dropdownAttachToBody && typeof document !== 'undefined' ? document.body : undefined
|
|
221
161
|
};
|
|
222
|
-
this.zone.runOutsideAngular(() => {
|
|
223
|
-
|
|
224
|
-
|
|
162
|
+
this.zone.runOutsideAngular(() => { this.iti = intlTelInput(this.inputRef.nativeElement, config); });
|
|
163
|
+
// expose z-index var to host (so CSS picks it up)
|
|
164
|
+
this.inputRef.nativeElement.style.setProperty('--tel-dd-z', String(this.dropdownZIndex));
|
|
225
165
|
}
|
|
226
166
|
reinitPlugin() {
|
|
227
|
-
this.destroyPlugin();
|
|
228
|
-
// keep current value
|
|
229
167
|
const current = this.currentRaw();
|
|
168
|
+
this.destroyPlugin();
|
|
230
169
|
this.initIntlTelInput().then(() => {
|
|
231
170
|
if (current) {
|
|
232
171
|
this.setInputValue(current);
|
|
@@ -239,12 +178,10 @@ class ngxsmkTelInputComponent {
|
|
|
239
178
|
this.iti.destroy();
|
|
240
179
|
this.iti = null;
|
|
241
180
|
}
|
|
242
|
-
// remove listeners by cloning node (simple & safe)
|
|
243
181
|
if (this.inputRef?.nativeElement) {
|
|
244
182
|
const el = this.inputRef.nativeElement;
|
|
245
183
|
const clone = el.cloneNode(true);
|
|
246
184
|
el.parentNode?.replaceChild(clone, el);
|
|
247
|
-
// update reference
|
|
248
185
|
this.inputRef.nativeElement = clone;
|
|
249
186
|
}
|
|
250
187
|
}
|
|
@@ -258,80 +195,129 @@ class ngxsmkTelInputComponent {
|
|
|
258
195
|
this.handleInput();
|
|
259
196
|
});
|
|
260
197
|
el.addEventListener('paste', () => queueMicrotask(() => this.handleInput()));
|
|
261
|
-
el.addEventListener('blur', () => this.
|
|
198
|
+
el.addEventListener('blur', () => this.onBlur());
|
|
262
199
|
});
|
|
263
200
|
}
|
|
201
|
+
onBlur() {
|
|
202
|
+
this.touched = true;
|
|
203
|
+
this.zone.run(() => this.onTouchedCb());
|
|
204
|
+
if (!this.formatOnBlur)
|
|
205
|
+
return;
|
|
206
|
+
const raw = this.currentRaw();
|
|
207
|
+
if (!raw)
|
|
208
|
+
return;
|
|
209
|
+
const parsed = this.tel.parse(raw, this.currentIso2());
|
|
210
|
+
if (this.nationalMode && parsed.national) {
|
|
211
|
+
this.setInputValue(parsed.national.replace(/\s{2,}/g, ' '));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
onFocus() {
|
|
215
|
+
if (this.selectOnFocus) {
|
|
216
|
+
const el = this.inputRef.nativeElement;
|
|
217
|
+
queueMicrotask(() => el.setSelectionRange(0, el.value.length));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
264
220
|
handleInput() {
|
|
265
221
|
const raw = this.currentRaw();
|
|
266
222
|
const iso2 = this.currentIso2();
|
|
267
223
|
const parsed = this.tel.parse(raw, iso2);
|
|
268
|
-
//
|
|
269
|
-
this.zone.run(() => this.
|
|
270
|
-
// Optional: present national vs. international in the box without fighting the user
|
|
271
|
-
// We only normalize whitespace; avoid aggressive reformatting to preserve caret.
|
|
224
|
+
this.zone.run(() => this.onChange(parsed.e164)); // E.164 or null
|
|
225
|
+
this.zone.run(() => this.inputChange.emit({ raw, e164: parsed.e164, iso2 }));
|
|
272
226
|
if (raw && this.nationalMode && parsed.national) {
|
|
273
|
-
// Replace double spaces etc. (intl-tel-input already styles)
|
|
274
227
|
const normalized = parsed.national.replace(/\s{2,}/g, ' ');
|
|
275
228
|
if (normalized !== raw)
|
|
276
229
|
this.setInputValue(normalized);
|
|
277
230
|
}
|
|
278
231
|
}
|
|
279
|
-
currentRaw() {
|
|
280
|
-
return (this.inputRef?.nativeElement.value ?? '').trim();
|
|
281
|
-
}
|
|
232
|
+
currentRaw() { return (this.inputRef?.nativeElement.value ?? '').trim(); }
|
|
282
233
|
currentIso2() {
|
|
283
234
|
const iso2 = (this.iti?.getSelectedCountryData?.().iso2 ?? this.initialCountry ?? 'US').toString().toUpperCase();
|
|
284
235
|
return iso2;
|
|
285
236
|
}
|
|
286
|
-
setInputValue(v) {
|
|
287
|
-
|
|
237
|
+
setInputValue(v) { this.inputRef.nativeElement.value = v ?? ''; }
|
|
238
|
+
get showError() {
|
|
239
|
+
const invalid = !!this.validate({});
|
|
240
|
+
return this.showErrorWhenTouched ? (this.touched && invalid) : invalid;
|
|
288
241
|
}
|
|
289
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
290
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type:
|
|
291
|
-
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() =>
|
|
292
|
-
{ provide: NG_VALIDATORS, useExisting: forwardRef(() =>
|
|
242
|
+
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 });
|
|
243
|
+
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", label: "label", hint: "hint", errorText: "errorText", size: "size", variant: "variant", showClear: "showClear", autoFocus: "autoFocus", selectOnFocus: "selectOnFocus", formatOnBlur: "formatOnBlur", showErrorWhenTouched: "showErrorWhenTouched", dropdownAttachToBody: "dropdownAttachToBody", dropdownZIndex: "dropdownZIndex" }, outputs: { countryChange: "countryChange", validityChange: "validityChange", inputChange: "inputChange" }, providers: [
|
|
244
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true },
|
|
245
|
+
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true }
|
|
293
246
|
], viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["telInput"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
294
|
-
<div class="
|
|
295
|
-
<
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
class="ngxsmk-tel-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
247
|
+
<div class="ngx-tel" [class.disabled]="disabled" [attr.data-size]="size" [attr.data-variant]="variant">
|
|
248
|
+
<label *ngIf="label" class="ngx-tel__label" [for]="resolvedId">{{ label }}</label>
|
|
249
|
+
|
|
250
|
+
<div class="ngx-tel__wrap" [class.has-error]="showError">
|
|
251
|
+
<div class="ngxsmk-tel-input__wrapper">
|
|
252
|
+
<input
|
|
253
|
+
#telInput
|
|
254
|
+
type="tel"
|
|
255
|
+
class="ngxsmk-tel-input__control"
|
|
256
|
+
[id]="resolvedId"
|
|
257
|
+
[attr.name]="name || null"
|
|
258
|
+
[attr.placeholder]="placeholder || null"
|
|
259
|
+
[attr.autocomplete]="autocomplete"
|
|
260
|
+
[disabled]="disabled"
|
|
261
|
+
[attr.aria-invalid]="showError ? 'true' : 'false'"
|
|
262
|
+
(blur)="onBlur()"
|
|
263
|
+
(focus)="onFocus()"
|
|
264
|
+
/>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<button *ngIf="showClear && currentRaw()"
|
|
268
|
+
type="button"
|
|
269
|
+
class="ngx-tel__clear"
|
|
270
|
+
(click)="clearInput()"
|
|
271
|
+
[attr.aria-label]="'Clear phone number'">
|
|
272
|
+
×
|
|
273
|
+
</button>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<div class="ngx-tel__hint" *ngIf="hint && !showError">{{ hint }}</div>
|
|
277
|
+
<div class="ngx-tel__error" *ngIf="showError">{{ errorText || 'Please enter a valid phone number.' }}</div>
|
|
306
278
|
</div>
|
|
307
|
-
`, isInline: true });
|
|
279
|
+
`, isInline: true, styles: [":host{--tel-bg: #fff;--tel-fg: #0f172a;--tel-border: #c0c0c0;--tel-border-hover: #9aa0a6;--tel-ring: #2563eb;--tel-placeholder: #9ca3af;--tel-error: #ef4444;--tel-radius: 12px;--tel-focus-shadow: 0 0 0 3px rgba(37, 99, 235, .25);--tel-dd-bg: var(--tel-bg);--tel-dd-border: var(--tel-border);--tel-dd-shadow: 0 24px 60px rgba(0,0,0,.18);--tel-dd-radius: 12px;--tel-dd-item-hover: rgba(37,99,235,.08);--tel-dd-z: 2000;--tel-dd-search-bg: rgba(148,163,184,.08);display:block}:host-context(.dark){--tel-bg: #0b0f17;--tel-fg: #e5e7eb;--tel-border: #334155;--tel-border-hover: #475569;--tel-ring: #60a5fa;--tel-placeholder: #94a3b8;--tel-dd-bg: #0f1521;--tel-dd-border: #324056;--tel-dd-search-bg: rgba(148,163,184,.12)}.ngx-tel{width:100%;color:var(--tel-fg)}.ngx-tel.disabled{opacity:.7;cursor:not-allowed}.ngx-tel__label{display:inline-block;margin-bottom:6px;font-size:.875rem;font-weight:500}.ngx-tel__wrap{position:relative}.ngxsmk-tel-input__wrapper,:host ::ng-deep .iti{width:100%}.ngxsmk-tel-input__control{width:100%;height:40px;font:inherit;color:var(--tel-fg);background:var(--tel-bg);border:1px solid var(--tel-border);border-radius:var(--tel-radius);padding:10px 40px 10px 12px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.ngxsmk-tel-input__control::placeholder{color:var(--tel-placeholder)}.ngxsmk-tel-input__control:hover{border-color:var(--tel-border-hover)}.ngxsmk-tel-input__control:focus{border-color:var(--tel-ring);box-shadow:var(--tel-focus-shadow)}[data-size=sm] .ngxsmk-tel-input__control{height:34px;font-size:13px;padding:6px 36px 6px 10px;border-radius:10px}[data-size=lg] .ngxsmk-tel-input__control{height:46px;font-size:16px;padding:12px 44px 12px 14px;border-radius:14px}[data-variant=filled] .ngxsmk-tel-input__control{background:#94a3b814}[data-variant=underline] .ngxsmk-tel-input__control{border:0;border-bottom:2px solid var(--tel-border);border-radius:0;padding-left:0;padding-right:34px}[data-variant=underline] .ngxsmk-tel-input__control:focus{border-bottom-color:var(--tel-ring);box-shadow:none}:host ::ng-deep .iti__flag-container{border-top-left-radius:var(--tel-radius);border-bottom-left-radius:var(--tel-radius);border:1px solid var(--tel-border);border-right:none;background:var(--tel-bg)}:host ::ng-deep .iti__selected-flag{height:100%;padding:0 10px;display:inline-flex;align-items:center}:host ::ng-deep .iti__country-list{background:var(--tel-dd-bg);border:1px solid var(--tel-dd-border);border-radius:var(--tel-dd-radius);box-shadow:var(--tel-dd-shadow);max-height:min(50vh,360px);overflow:auto;padding:6px 0;width:max(280px,100%);z-index:var(--tel-dd-z)}:host ::ng-deep .iti--container .iti__country-list{z-index:var(--tel-dd-z)}:host ::ng-deep .iti__search-input{position:sticky;top:0;margin:0;padding:10px 12px;width:100%;border:0;border-bottom:1px solid var(--tel-dd-border);outline:none;background:var(--tel-dd-search-bg);color:var(--tel-fg)}:host ::ng-deep .iti__search-input::placeholder{color:var(--tel-placeholder)}:host ::ng-deep .iti__country{display:grid;grid-template-columns:28px 1fr auto;align-items:center;column-gap:.5rem;padding:10px 12px;cursor:pointer}:host ::ng-deep .iti__flag-box{width:28px;display:inline-flex;justify-content:center}:host ::ng-deep .iti__country-name{color:var(--tel-fg)}:host ::ng-deep .iti__dial-code{color:var(--tel-placeholder);font-weight:600;margin-left:10px}:host ::ng-deep .iti__country:hover,:host ::ng-deep .iti__country.iti__highlight{background:var(--tel-dd-item-hover)}:host ::ng-deep .iti__country:focus{outline:2px solid var(--tel-ring);outline-offset:-2px}:host ::ng-deep .iti__divider{margin:6px 0;border-top:1px dashed var(--tel-dd-border)}:host ::ng-deep .iti--separate-dial-code .ngxsmk-tel-input__control{padding-left:56px}:host ::ng-deep .iti__country-list::-webkit-scrollbar{width:10px}:host ::ng-deep .iti__country-list::-webkit-scrollbar-thumb{background:#94a3b866;border-radius:8px}:host ::ng-deep .iti__country-list::-webkit-scrollbar-track{background:transparent}@media (max-width: 480px){:host ::ng-deep .iti__country-list{width:100vw;max-width:100vw}}.ngx-tel__clear{position:absolute;right:8px;top:50%;transform:translateY(-50%);border:0;background:transparent;font-size:18px;line-height:1;width:28px;height:28px;border-radius:50%;cursor:pointer;color:var(--tel-placeholder)}.ngx-tel__clear:hover{background:#94a3b826}.ngx-tel__hint{margin-top:6px;font-size:12px;color:var(--tel-placeholder)}.ngx-tel__error{margin-top:6px;font-size:12px;color:var(--tel-error)}.ngx-tel__wrap.has-error .ngxsmk-tel-input__control{border-color:var(--tel-error);box-shadow:0 0 0 3px #ef444426}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
308
280
|
}
|
|
309
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type:
|
|
281
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxsmkTelInputComponent, decorators: [{
|
|
310
282
|
type: Component,
|
|
311
|
-
args: [{
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
283
|
+
args: [{ selector: 'ngxsmk-tel-input', standalone: true, imports: [NgIf], template: `
|
|
284
|
+
<div class="ngx-tel" [class.disabled]="disabled" [attr.data-size]="size" [attr.data-variant]="variant">
|
|
285
|
+
<label *ngIf="label" class="ngx-tel__label" [for]="resolvedId">{{ label }}</label>
|
|
286
|
+
|
|
287
|
+
<div class="ngx-tel__wrap" [class.has-error]="showError">
|
|
288
|
+
<div class="ngxsmk-tel-input__wrapper">
|
|
289
|
+
<input
|
|
290
|
+
#telInput
|
|
291
|
+
type="tel"
|
|
292
|
+
class="ngxsmk-tel-input__control"
|
|
293
|
+
[id]="resolvedId"
|
|
294
|
+
[attr.name]="name || null"
|
|
295
|
+
[attr.placeholder]="placeholder || null"
|
|
296
|
+
[attr.autocomplete]="autocomplete"
|
|
297
|
+
[disabled]="disabled"
|
|
298
|
+
[attr.aria-invalid]="showError ? 'true' : 'false'"
|
|
299
|
+
(blur)="onBlur()"
|
|
300
|
+
(focus)="onFocus()"
|
|
301
|
+
/>
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<button *ngIf="showClear && currentRaw()"
|
|
305
|
+
type="button"
|
|
306
|
+
class="ngx-tel__clear"
|
|
307
|
+
(click)="clearInput()"
|
|
308
|
+
[attr.aria-label]="'Clear phone number'">
|
|
309
|
+
×
|
|
310
|
+
</button>
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
<div class="ngx-tel__hint" *ngIf="hint && !showError">{{ hint }}</div>
|
|
314
|
+
<div class="ngx-tel__error" *ngIf="showError">{{ errorText || 'Please enter a valid phone number.' }}</div>
|
|
327
315
|
</div>
|
|
328
|
-
`,
|
|
329
|
-
|
|
330
|
-
{ provide:
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}]
|
|
334
|
-
}], ctorParameters: () => [{ type: i0.NgZone }, { type: ngxsmkTelInputService }, { type: Object, decorators: [{
|
|
316
|
+
`, providers: [
|
|
317
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true },
|
|
318
|
+
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true }
|
|
319
|
+
], styles: [":host{--tel-bg: #fff;--tel-fg: #0f172a;--tel-border: #c0c0c0;--tel-border-hover: #9aa0a6;--tel-ring: #2563eb;--tel-placeholder: #9ca3af;--tel-error: #ef4444;--tel-radius: 12px;--tel-focus-shadow: 0 0 0 3px rgba(37, 99, 235, .25);--tel-dd-bg: var(--tel-bg);--tel-dd-border: var(--tel-border);--tel-dd-shadow: 0 24px 60px rgba(0,0,0,.18);--tel-dd-radius: 12px;--tel-dd-item-hover: rgba(37,99,235,.08);--tel-dd-z: 2000;--tel-dd-search-bg: rgba(148,163,184,.08);display:block}:host-context(.dark){--tel-bg: #0b0f17;--tel-fg: #e5e7eb;--tel-border: #334155;--tel-border-hover: #475569;--tel-ring: #60a5fa;--tel-placeholder: #94a3b8;--tel-dd-bg: #0f1521;--tel-dd-border: #324056;--tel-dd-search-bg: rgba(148,163,184,.12)}.ngx-tel{width:100%;color:var(--tel-fg)}.ngx-tel.disabled{opacity:.7;cursor:not-allowed}.ngx-tel__label{display:inline-block;margin-bottom:6px;font-size:.875rem;font-weight:500}.ngx-tel__wrap{position:relative}.ngxsmk-tel-input__wrapper,:host ::ng-deep .iti{width:100%}.ngxsmk-tel-input__control{width:100%;height:40px;font:inherit;color:var(--tel-fg);background:var(--tel-bg);border:1px solid var(--tel-border);border-radius:var(--tel-radius);padding:10px 40px 10px 12px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.ngxsmk-tel-input__control::placeholder{color:var(--tel-placeholder)}.ngxsmk-tel-input__control:hover{border-color:var(--tel-border-hover)}.ngxsmk-tel-input__control:focus{border-color:var(--tel-ring);box-shadow:var(--tel-focus-shadow)}[data-size=sm] .ngxsmk-tel-input__control{height:34px;font-size:13px;padding:6px 36px 6px 10px;border-radius:10px}[data-size=lg] .ngxsmk-tel-input__control{height:46px;font-size:16px;padding:12px 44px 12px 14px;border-radius:14px}[data-variant=filled] .ngxsmk-tel-input__control{background:#94a3b814}[data-variant=underline] .ngxsmk-tel-input__control{border:0;border-bottom:2px solid var(--tel-border);border-radius:0;padding-left:0;padding-right:34px}[data-variant=underline] .ngxsmk-tel-input__control:focus{border-bottom-color:var(--tel-ring);box-shadow:none}:host ::ng-deep .iti__flag-container{border-top-left-radius:var(--tel-radius);border-bottom-left-radius:var(--tel-radius);border:1px solid var(--tel-border);border-right:none;background:var(--tel-bg)}:host ::ng-deep .iti__selected-flag{height:100%;padding:0 10px;display:inline-flex;align-items:center}:host ::ng-deep .iti__country-list{background:var(--tel-dd-bg);border:1px solid var(--tel-dd-border);border-radius:var(--tel-dd-radius);box-shadow:var(--tel-dd-shadow);max-height:min(50vh,360px);overflow:auto;padding:6px 0;width:max(280px,100%);z-index:var(--tel-dd-z)}:host ::ng-deep .iti--container .iti__country-list{z-index:var(--tel-dd-z)}:host ::ng-deep .iti__search-input{position:sticky;top:0;margin:0;padding:10px 12px;width:100%;border:0;border-bottom:1px solid var(--tel-dd-border);outline:none;background:var(--tel-dd-search-bg);color:var(--tel-fg)}:host ::ng-deep .iti__search-input::placeholder{color:var(--tel-placeholder)}:host ::ng-deep .iti__country{display:grid;grid-template-columns:28px 1fr auto;align-items:center;column-gap:.5rem;padding:10px 12px;cursor:pointer}:host ::ng-deep .iti__flag-box{width:28px;display:inline-flex;justify-content:center}:host ::ng-deep .iti__country-name{color:var(--tel-fg)}:host ::ng-deep .iti__dial-code{color:var(--tel-placeholder);font-weight:600;margin-left:10px}:host ::ng-deep .iti__country:hover,:host ::ng-deep .iti__country.iti__highlight{background:var(--tel-dd-item-hover)}:host ::ng-deep .iti__country:focus{outline:2px solid var(--tel-ring);outline-offset:-2px}:host ::ng-deep .iti__divider{margin:6px 0;border-top:1px dashed var(--tel-dd-border)}:host ::ng-deep .iti--separate-dial-code .ngxsmk-tel-input__control{padding-left:56px}:host ::ng-deep .iti__country-list::-webkit-scrollbar{width:10px}:host ::ng-deep .iti__country-list::-webkit-scrollbar-thumb{background:#94a3b866;border-radius:8px}:host ::ng-deep .iti__country-list::-webkit-scrollbar-track{background:transparent}@media (max-width: 480px){:host ::ng-deep .iti__country-list{width:100vw;max-width:100vw}}.ngx-tel__clear{position:absolute;right:8px;top:50%;transform:translateY(-50%);border:0;background:transparent;font-size:18px;line-height:1;width:28px;height:28px;border-radius:50%;cursor:pointer;color:var(--tel-placeholder)}.ngx-tel__clear:hover{background:#94a3b826}.ngx-tel__hint{margin-top:6px;font-size:12px;color:var(--tel-placeholder)}.ngx-tel__error{margin-top:6px;font-size:12px;color:var(--tel-error)}.ngx-tel__wrap.has-error .ngxsmk-tel-input__control{border-color:var(--tel-error);box-shadow:0 0 0 3px #ef444426}\n"] }]
|
|
320
|
+
}], ctorParameters: () => [{ type: i0.NgZone }, { type: NgxsmkTelInputService }, { type: Object, decorators: [{
|
|
335
321
|
type: Inject,
|
|
336
322
|
args: [PLATFORM_ID]
|
|
337
323
|
}] }], propDecorators: { inputRef: [{
|
|
@@ -359,19 +345,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
359
345
|
type: Input
|
|
360
346
|
}], disabled: [{
|
|
361
347
|
type: Input
|
|
348
|
+
}], label: [{
|
|
349
|
+
type: Input
|
|
350
|
+
}], hint: [{
|
|
351
|
+
type: Input
|
|
352
|
+
}], errorText: [{
|
|
353
|
+
type: Input
|
|
354
|
+
}], size: [{
|
|
355
|
+
type: Input
|
|
356
|
+
}], variant: [{
|
|
357
|
+
type: Input
|
|
358
|
+
}], showClear: [{
|
|
359
|
+
type: Input
|
|
360
|
+
}], autoFocus: [{
|
|
361
|
+
type: Input
|
|
362
|
+
}], selectOnFocus: [{
|
|
363
|
+
type: Input
|
|
364
|
+
}], formatOnBlur: [{
|
|
365
|
+
type: Input
|
|
366
|
+
}], showErrorWhenTouched: [{
|
|
367
|
+
type: Input
|
|
368
|
+
}], dropdownAttachToBody: [{
|
|
369
|
+
type: Input
|
|
370
|
+
}], dropdownZIndex: [{
|
|
371
|
+
type: Input
|
|
362
372
|
}], countryChange: [{
|
|
363
373
|
type: Output
|
|
364
374
|
}], validityChange: [{
|
|
365
375
|
type: Output
|
|
376
|
+
}], inputChange: [{
|
|
377
|
+
type: Output
|
|
366
378
|
}] } });
|
|
367
379
|
|
|
368
|
-
/*
|
|
369
|
-
* Public API Surface of ngxsmk-tel-input
|
|
370
|
-
*/
|
|
371
|
-
|
|
372
380
|
/**
|
|
373
381
|
* Generated bundle index. Do not edit.
|
|
374
382
|
*/
|
|
375
383
|
|
|
376
|
-
export {
|
|
384
|
+
export { NgxsmkTelInputComponent, NgxsmkTelInputService };
|
|
377
385
|
//# sourceMappingURL=ngxsmk-tel-input.mjs.map
|
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"ngxsmk-tel-input.mjs","sources":["../../src/lib/ngxsmk-tel-input.service.ts","../../src/lib/ngxsmk-tel-input.component.ts","../../src/ngxsmk-tel-input.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\r\nimport { parsePhoneNumberFromString, type CountryCode } from 'libphonenumber-js';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class NgxsmkTelInputService {\r\n parse(input: string, iso2: CountryCode): { e164: string | null; national: string | null; isValid: boolean } {\r\n const phone = parsePhoneNumberFromString(input, iso2);\r\n if (!phone) return { e164: null, national: null, isValid: false };\r\n const isValid = phone.isValid();\r\n return { e164: isValid ? phone.number : null, national: phone.formatNational(), isValid };\r\n }\r\n\r\n isValid(input: string, iso2: CountryCode): boolean {\r\n const phone = parsePhoneNumberFromString(input, iso2);\r\n return !!phone && phone.isValid();\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, NgIf } 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;\r\n\r\n@Component({\r\n selector: 'ngxsmk-tel-input',\r\n standalone: true,\r\n imports: [NgIf],\r\n template: `\r\n <div class=\"ngx-tel\" [class.disabled]=\"disabled\" [attr.data-size]=\"size\" [attr.data-variant]=\"variant\">\r\n <label *ngIf=\"label\" class=\"ngx-tel__label\" [for]=\"resolvedId\">{{ label }}</label>\r\n\r\n <div class=\"ngx-tel__wrap\" [class.has-error]=\"showError\">\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]=\"resolvedId\"\r\n [attr.name]=\"name || null\"\r\n [attr.placeholder]=\"placeholder || null\"\r\n [attr.autocomplete]=\"autocomplete\"\r\n [disabled]=\"disabled\"\r\n [attr.aria-invalid]=\"showError ? 'true' : 'false'\"\r\n (blur)=\"onBlur()\"\r\n (focus)=\"onFocus()\"\r\n />\r\n </div>\r\n\r\n <button *ngIf=\"showClear && currentRaw()\"\r\n type=\"button\"\r\n class=\"ngx-tel__clear\"\r\n (click)=\"clearInput()\"\r\n [attr.aria-label]=\"'Clear phone number'\">\r\n ×\r\n </button>\r\n </div>\r\n\r\n <div class=\"ngx-tel__hint\" *ngIf=\"hint && !showError\">{{ hint }}</div>\r\n <div class=\"ngx-tel__error\" *ngIf=\"showError\">{{ errorText || 'Please enter a valid phone number.' }}</div>\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 styles: [`\r\n /* ---------- Theme tokens ---------- */\r\n :host {\r\n --tel-bg: #fff;\r\n --tel-fg: #0f172a;\r\n --tel-border: #c0c0c0;\r\n --tel-border-hover: #9aa0a6;\r\n --tel-ring: #2563eb;\r\n --tel-placeholder: #9ca3af;\r\n --tel-error: #ef4444;\r\n --tel-radius: 12px;\r\n --tel-focus-shadow: 0 0 0 3px rgba(37, 99, 235, .25);\r\n\r\n /* dropdown tokens */\r\n --tel-dd-bg: var(--tel-bg);\r\n --tel-dd-border: var(--tel-border);\r\n --tel-dd-shadow: 0 24px 60px rgba(0,0,0,.18);\r\n --tel-dd-radius: 12px;\r\n --tel-dd-item-hover: rgba(37,99,235,.08);\r\n --tel-dd-z: 2000;\r\n --tel-dd-search-bg: rgba(148,163,184,.08);\r\n\r\n display: block;\r\n }\r\n :host-context(.dark) {\r\n --tel-bg: #0b0f17;\r\n --tel-fg: #e5e7eb;\r\n --tel-border: #334155;\r\n --tel-border-hover: #475569;\r\n --tel-ring: #60a5fa;\r\n --tel-placeholder: #94a3b8;\r\n\r\n --tel-dd-bg: #0f1521;\r\n --tel-dd-border: #324056;\r\n --tel-dd-search-bg: rgba(148,163,184,.12);\r\n }\r\n\r\n /* ---------- Structure ---------- */\r\n .ngx-tel { width: 100%; color: var(--tel-fg); }\r\n .ngx-tel.disabled { opacity: .7; cursor: not-allowed; }\r\n\r\n .ngx-tel__label { display: inline-block; margin-bottom: 6px; font-size: .875rem; font-weight: 500; }\r\n\r\n .ngx-tel__wrap { position: relative; }\r\n\r\n .ngxsmk-tel-input__wrapper,\r\n :host ::ng-deep .iti { width: 100%; }\r\n\r\n .ngxsmk-tel-input__control {\r\n width: 100%;\r\n height: 40px;\r\n font: inherit;\r\n color: var(--tel-fg);\r\n background: var(--tel-bg);\r\n border: 1px solid var(--tel-border);\r\n border-radius: var(--tel-radius);\r\n padding: 10px 40px 10px 12px; /* room for clear button */\r\n outline: none;\r\n transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;\r\n }\r\n .ngxsmk-tel-input__control::placeholder { color: var(--tel-placeholder); }\r\n .ngxsmk-tel-input__control:hover { border-color: var(--tel-border-hover); }\r\n .ngxsmk-tel-input__control:focus { border-color: var(--tel-ring); box-shadow: var(--tel-focus-shadow); }\r\n\r\n /* Size presets */\r\n [data-size=\"sm\"] .ngxsmk-tel-input__control { height: 34px; font-size: 13px; padding: 6px 36px 6px 10px; border-radius: 10px; }\r\n [data-size=\"lg\"] .ngxsmk-tel-input__control { height: 46px; font-size: 16px; padding: 12px 44px 12px 14px; border-radius: 14px; }\r\n\r\n /* Variants */\r\n [data-variant=\"filled\"] .ngxsmk-tel-input__control { background: rgba(148, 163, 184, .08); }\r\n [data-variant=\"underline\"] .ngxsmk-tel-input__control { border: 0; border-bottom: 2px solid var(--tel-border); border-radius: 0; padding-left: 0; padding-right: 34px; }\r\n [data-variant=\"underline\"] .ngxsmk-tel-input__control:focus { border-bottom-color: var(--tel-ring); box-shadow: none; }\r\n\r\n /* ---------- intl-tel-input dropdown (deep selectors) ---------- */\r\n :host ::ng-deep .iti__flag-container {\r\n border-top-left-radius: var(--tel-radius);\r\n border-bottom-left-radius: var(--tel-radius);\r\n border: 1px solid var(--tel-border);\r\n border-right: none;\r\n background: var(--tel-bg);\r\n }\r\n :host ::ng-deep .iti__selected-flag { height: 100%; padding: 0 10px; display: inline-flex; align-items: center; }\r\n\r\n /* Core dropdown panel */\r\n :host ::ng-deep .iti__country-list {\r\n background: var(--tel-dd-bg);\r\n border: 1px solid var(--tel-dd-border);\r\n border-radius: var(--tel-dd-radius);\r\n box-shadow: var(--tel-dd-shadow);\r\n max-height: min(50vh, 360px);\r\n overflow: auto;\r\n padding: 6px 0;\r\n width: max(280px, 100%);\r\n z-index: var(--tel-dd-z);\r\n }\r\n /* When attached to <body>, it's wrapped in .iti--container */\r\n :host ::ng-deep .iti--container .iti__country-list {\r\n z-index: var(--tel-dd-z);\r\n }\r\n\r\n /* Search input (sticky header) */\r\n :host ::ng-deep .iti__search-input {\r\n position: sticky; top: 0;\r\n margin: 0; padding: 10px 12px;\r\n width: 100%;\r\n border: 0; border-bottom: 1px solid var(--tel-dd-border);\r\n outline: none;\r\n background: var(--tel-dd-search-bg);\r\n color: var(--tel-fg);\r\n }\r\n :host ::ng-deep .iti__search-input::placeholder { color: var(--tel-placeholder); }\r\n\r\n /* Rows: flag | country name | dial code (right) */\r\n :host ::ng-deep .iti__country {\r\n display: grid;\r\n grid-template-columns: 28px 1fr auto;\r\n align-items: center;\r\n column-gap: .5rem;\r\n padding: 10px 12px;\r\n cursor: pointer;\r\n }\r\n :host ::ng-deep .iti__flag-box { width: 28px; display: inline-flex; justify-content: center; }\r\n :host ::ng-deep .iti__country-name { color: var(--tel-fg); }\r\n :host ::ng-deep .iti__dial-code { color: var(--tel-placeholder); font-weight: 600; margin-left: 10px; }\r\n :host ::ng-deep .iti__country:hover,\r\n :host ::ng-deep .iti__country.iti__highlight { background: var(--tel-dd-item-hover); }\r\n :host ::ng-deep .iti__country:focus { outline: 2px solid var(--tel-ring); outline-offset: -2px; }\r\n\r\n :host ::ng-deep .iti__divider { margin: 6px 0; border-top: 1px dashed var(--tel-dd-border); }\r\n\r\n /* Separate dial code pushes input text */\r\n :host ::ng-deep .iti--separate-dial-code .ngxsmk-tel-input__control { padding-left: 56px; }\r\n\r\n /* Custom scrollbar (WebKit/Chromium) */\r\n :host ::ng-deep .iti__country-list::-webkit-scrollbar { width: 10px; }\r\n :host ::ng-deep .iti__country-list::-webkit-scrollbar-thumb { background: rgba(148,163,184,.4); border-radius: 8px; }\r\n :host ::ng-deep .iti__country-list::-webkit-scrollbar-track { background: transparent; }\r\n\r\n /* Mobile tweak: make it breathe */\r\n @media (max-width: 480px) {\r\n :host ::ng-deep .iti__country-list { width: 100vw; max-width: 100vw; }\r\n }\r\n\r\n /* Clear button */\r\n .ngx-tel__clear {\r\n position: absolute; right: 8px; top: 50%; transform: translateY(-50%);\r\n border: 0; background: transparent; font-size: 18px; line-height: 1;\r\n width: 28px; height: 28px; border-radius: 50%; cursor: pointer; color: var(--tel-placeholder);\r\n }\r\n .ngx-tel__clear:hover { background: rgba(148, 163, 184, .15); }\r\n\r\n /* Hint & Error */\r\n .ngx-tel__hint { margin-top: 6px; font-size: 12px; color: var(--tel-placeholder); }\r\n .ngx-tel__error { margin-top: 6px; font-size: 12px; color: var(--tel-error); }\r\n .ngx-tel__wrap.has-error .ngxsmk-tel-input__control { border-color: var(--tel-error); box-shadow: 0 0 0 3px rgba(239, 68, 68, .15); }\r\n `]\r\n})\r\nexport class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {\r\n @ViewChild('telInput', { static: true }) inputRef!: ElementRef<HTMLInputElement>;\r\n\r\n /* Existing inputs */\r\n @Input() initialCountry: CountryCode | 'auto' = 'US';\r\n @Input() preferredCountries: CountryCode[] = ['US', 'GB'];\r\n @Input() onlyCountries?: CountryCode[];\r\n @Input() nationalMode = false;\r\n @Input() separateDialCode = false;\r\n @Input() allowDropdown = true;\r\n\r\n @Input() placeholder = 'Enter phone number';\r\n @Input() autocomplete = 'tel';\r\n @Input() name?: string;\r\n @Input() inputId?: string;\r\n @Input() disabled = false;\r\n\r\n /* New UI/UX inputs */\r\n @Input() label?: string;\r\n @Input() hint?: string;\r\n @Input() errorText?: string;\r\n @Input() size: 'sm' | 'md' | 'lg' = 'md';\r\n @Input() variant: 'outline' | 'filled' | 'underline' = 'outline';\r\n @Input() showClear = true;\r\n @Input() autoFocus = false;\r\n @Input() selectOnFocus = false;\r\n @Input() formatOnBlur = true;\r\n @Input() showErrorWhenTouched = true;\r\n\r\n /** Dropdown plumbing */\r\n @Input() dropdownAttachToBody = true; // append dropdown to <body> (escapes overflow/clip)\r\n @Input() dropdownZIndex = 2000; // used by CSS var --tel-dd-z\r\n\r\n /* Outputs */\r\n @Output() countryChange = new EventEmitter<{ iso2: CountryCode }>();\r\n @Output() validityChange = new EventEmitter<boolean>();\r\n @Output() inputChange = new EventEmitter<{ raw: string; e164: string | null; iso2: CountryCode }>();\r\n\r\n /* Internal */\r\n private iti: IntlTelInstance | null = null;\r\n private onChange: (val: string | null) => void = () => {};\r\n private onTouchedCb: () => void = () => {};\r\n private lastEmittedValid = false;\r\n private pendingWrite: string | null = null;\r\n private touched = false;\r\n\r\n readonly resolvedId = (() => 'tel-' + Math.random().toString(36).slice(2))();\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 async ngAfterViewInit() {\r\n if (!isPlatformBrowser(this.platformId)) return;\r\n\r\n // set z-index via CSS var\r\n (this as any).constructor; // no-op to keep TS calm\r\n (this.inputRef.nativeElement.closest(':host') as any);\r\n\r\n await this.initIntlTelInput();\r\n this.bindDomListeners();\r\n\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 if (this.autoFocus) setTimeout(() => this.focus(), 0);\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n if (!isPlatformBrowser(this.platformId)) return;\r\n const configChanged = ['initialCountry','preferredCountries','onlyCountries','separateDialCode','allowDropdown','nationalMode']\r\n .some(k => k in changes && !changes[k].firstChange);\r\n if (configChanged && this.iti) this.reinitPlugin();\r\n }\r\n\r\n ngOnDestroy(): void { this.destroyPlugin(); }\r\n\r\n // ----- CVA -----\r\n writeValue(val: string | null): void {\r\n if (!this.inputRef) return;\r\n if (!this.iti) { this.pendingWrite = val ?? ''; return; }\r\n this.setInputValue(val ?? '');\r\n }\r\n registerOnChange(fn: any): void { this.onChange = fn; }\r\n registerOnTouched(fn: any): void { this.onTouchedCb = fn; }\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;\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 if (this.selectOnFocus) {\r\n const el = this.inputRef.nativeElement;\r\n queueMicrotask(() => el.setSelectionRange(0, el.value.length));\r\n }\r\n }\r\n selectCountry(iso2: CountryCode): void {\r\n if (this.iti) { this.iti.setCountry(iso2.toLowerCase()); this.handleInput(); }\r\n }\r\n clearInput() {\r\n this.setInputValue(''); this.handleInput(); this.inputRef.nativeElement.focus();\r\n }\r\n\r\n // ----- Plugin wiring -----\r\n private async initIntlTelInput() {\r\n const [{ default: intlTelInput }] = await Promise.all([ import('intl-tel-input') ]);\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 geoIpLookup: (cb: (iso2: string) => void) => cb('us'),\r\n utilsScript: undefined,\r\n dropdownContainer: this.dropdownAttachToBody && typeof document !== 'undefined' ? document.body : undefined\r\n };\r\n this.zone.runOutsideAngular(() => { this.iti = intlTelInput(this.inputRef.nativeElement, config); });\r\n\r\n // expose z-index var to host (so CSS picks it up)\r\n (this.inputRef.nativeElement as HTMLElement).style.setProperty('--tel-dd-z', String(this.dropdownZIndex));\r\n }\r\n\r\n private reinitPlugin() {\r\n const current = this.currentRaw();\r\n this.destroyPlugin();\r\n this.initIntlTelInput().then(() => {\r\n if (current) { this.setInputValue(current); this.handleInput(); }\r\n });\r\n }\r\n\r\n private destroyPlugin() {\r\n if (this.iti) { this.iti.destroy(); this.iti = null; }\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 (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 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.onBlur());\r\n });\r\n }\r\n\r\n onBlur() {\r\n this.touched = true;\r\n this.zone.run(() => this.onTouchedCb());\r\n if (!this.formatOnBlur) return;\r\n const raw = this.currentRaw();\r\n if (!raw) return;\r\n const parsed = this.tel.parse(raw, this.currentIso2());\r\n if (this.nationalMode && parsed.national) {\r\n this.setInputValue(parsed.national.replace(/\\s{2,}/g, ' '));\r\n }\r\n }\r\n\r\n onFocus() {\r\n if (this.selectOnFocus) {\r\n const el = this.inputRef.nativeElement;\r\n queueMicrotask(() => el.setSelectionRange(0, el.value.length));\r\n }\r\n }\r\n\r\n private handleInput() {\r\n const raw = this.currentRaw();\r\n const iso2 = this.currentIso2();\r\n const parsed = this.tel.parse(raw, iso2);\r\n this.zone.run(() => this.onChange(parsed.e164)); // E.164 or null\r\n this.zone.run(() => this.inputChange.emit({ raw, e164: parsed.e164, iso2 }));\r\n if (raw && this.nationalMode && parsed.national) {\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 currentRaw(): string { return (this.inputRef?.nativeElement.value ?? '').trim(); }\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 private setInputValue(v: string) { this.inputRef.nativeElement.value = v ?? ''; }\r\n\r\n get showError(): boolean {\r\n const invalid = !!this.validate({} as AbstractControl);\r\n return this.showErrorWhenTouched ? (this.touched && invalid) : invalid;\r\n }\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,42 +1,54 @@
|
|
|
1
1
|
import { AfterViewInit, ElementRef, EventEmitter, NgZone, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
|
|
2
2
|
import { AbstractControl, ControlValueAccessor, ValidationErrors } from '@angular/forms';
|
|
3
3
|
import type { CountryCode } from 'libphonenumber-js';
|
|
4
|
-
import {
|
|
4
|
+
import { NgxsmkTelInputService } from './ngxsmk-tel-input.service';
|
|
5
5
|
import * as i0 from "@angular/core";
|
|
6
|
-
export declare class
|
|
6
|
+
export declare class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {
|
|
7
7
|
private readonly zone;
|
|
8
8
|
private readonly tel;
|
|
9
9
|
private readonly platformId;
|
|
10
10
|
inputRef: ElementRef<HTMLInputElement>;
|
|
11
|
-
/** Initial country (ISO2) or 'auto' to pick a default via geoIpLookup stub */
|
|
12
11
|
initialCountry: CountryCode | 'auto';
|
|
13
|
-
/** Preferred countries on top of the dropdown */
|
|
14
12
|
preferredCountries: CountryCode[];
|
|
15
|
-
/** Limit to these countries only (omit for all) */
|
|
16
13
|
onlyCountries?: CountryCode[];
|
|
17
|
-
/** Show national numbers instead of E.164 in the box (emits E.164) */
|
|
18
14
|
nationalMode: boolean;
|
|
19
|
-
/** Show the dial code separately before the input (intl-tel-input option) */
|
|
20
15
|
separateDialCode: boolean;
|
|
21
|
-
/** Allow opening the country dropdown */
|
|
22
16
|
allowDropdown: boolean;
|
|
23
|
-
/** Plain input attributes */
|
|
24
17
|
placeholder: string;
|
|
25
18
|
autocomplete: string;
|
|
26
19
|
name?: string;
|
|
27
20
|
inputId?: string;
|
|
28
|
-
/** Disabled state (also settable via Angular Forms) */
|
|
29
21
|
disabled: boolean;
|
|
22
|
+
label?: string;
|
|
23
|
+
hint?: string;
|
|
24
|
+
errorText?: string;
|
|
25
|
+
size: 'sm' | 'md' | 'lg';
|
|
26
|
+
variant: 'outline' | 'filled' | 'underline';
|
|
27
|
+
showClear: boolean;
|
|
28
|
+
autoFocus: boolean;
|
|
29
|
+
selectOnFocus: boolean;
|
|
30
|
+
formatOnBlur: boolean;
|
|
31
|
+
showErrorWhenTouched: boolean;
|
|
32
|
+
/** Dropdown plumbing */
|
|
33
|
+
dropdownAttachToBody: boolean;
|
|
34
|
+
dropdownZIndex: number;
|
|
30
35
|
countryChange: EventEmitter<{
|
|
31
36
|
iso2: CountryCode;
|
|
32
37
|
}>;
|
|
33
38
|
validityChange: EventEmitter<boolean>;
|
|
39
|
+
inputChange: EventEmitter<{
|
|
40
|
+
raw: string;
|
|
41
|
+
e164: string | null;
|
|
42
|
+
iso2: CountryCode;
|
|
43
|
+
}>;
|
|
34
44
|
private iti;
|
|
35
45
|
private onChange;
|
|
36
|
-
private
|
|
46
|
+
private onTouchedCb;
|
|
37
47
|
private lastEmittedValid;
|
|
38
48
|
private pendingWrite;
|
|
39
|
-
|
|
49
|
+
private touched;
|
|
50
|
+
readonly resolvedId: string;
|
|
51
|
+
constructor(zone: NgZone, tel: NgxsmkTelInputService, platformId: Object);
|
|
40
52
|
ngAfterViewInit(): Promise<void>;
|
|
41
53
|
ngOnChanges(changes: SimpleChanges): void;
|
|
42
54
|
ngOnDestroy(): void;
|
|
@@ -47,15 +59,18 @@ export declare class ngxsmkTelInputComponent implements AfterViewInit, OnChanges
|
|
|
47
59
|
validate(_: AbstractControl): ValidationErrors | null;
|
|
48
60
|
focus(): void;
|
|
49
61
|
selectCountry(iso2: CountryCode): void;
|
|
50
|
-
|
|
62
|
+
clearInput(): void;
|
|
51
63
|
private initIntlTelInput;
|
|
52
64
|
private reinitPlugin;
|
|
53
65
|
private destroyPlugin;
|
|
54
66
|
private bindDomListeners;
|
|
67
|
+
onBlur(): void;
|
|
68
|
+
onFocus(): void;
|
|
55
69
|
private handleInput;
|
|
56
|
-
|
|
70
|
+
currentRaw(): string;
|
|
57
71
|
private currentIso2;
|
|
58
72
|
private setInputValue;
|
|
59
|
-
|
|
60
|
-
static
|
|
73
|
+
get showError(): boolean;
|
|
74
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NgxsmkTelInputComponent, never>;
|
|
75
|
+
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; }; "label": { "alias": "label"; "required": false; }; "hint": { "alias": "hint"; "required": false; }; "errorText": { "alias": "errorText"; "required": false; }; "size": { "alias": "size"; "required": false; }; "variant": { "alias": "variant"; "required": false; }; "showClear": { "alias": "showClear"; "required": false; }; "autoFocus": { "alias": "autoFocus"; "required": false; }; "selectOnFocus": { "alias": "selectOnFocus"; "required": false; }; "formatOnBlur": { "alias": "formatOnBlur"; "required": false; }; "showErrorWhenTouched": { "alias": "showErrorWhenTouched"; "required": false; }; "dropdownAttachToBody": { "alias": "dropdownAttachToBody"; "required": false; }; "dropdownZIndex": { "alias": "dropdownZIndex"; "required": false; }; }, { "countryChange": "countryChange"; "validityChange": "validityChange"; "inputChange": "inputChange"; }, never, never, true, never>;
|
|
61
76
|
}
|
|
@@ -1,56 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { CountryCode, PhoneNumber } from 'libphonenumber-js';
|
|
1
|
+
import { type CountryCode } from 'libphonenumber-js';
|
|
3
2
|
import * as i0 from "@angular/core";
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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>;
|
|
3
|
+
export declare class NgxsmkTelInputService {
|
|
4
|
+
parse(input: string, iso2: CountryCode): {
|
|
5
|
+
e164: string | null;
|
|
6
|
+
national: string | null;
|
|
7
|
+
isValid: boolean;
|
|
8
|
+
};
|
|
9
|
+
isValid(input: string, iso2: CountryCode): boolean;
|
|
10
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NgxsmkTelInputService, never>;
|
|
11
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<NgxsmkTelInputService>;
|
|
56
12
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngxsmk-tel-input",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "Angular telephone input with intl-tel-input + libphonenumber-js",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
4
11
|
"peerDependencies": {
|
|
5
|
-
"@angular/
|
|
6
|
-
"@angular/
|
|
12
|
+
"@angular/core": ">=17",
|
|
13
|
+
"@angular/common": ">=17",
|
|
14
|
+
"@angular/forms": ">=17",
|
|
15
|
+
"rxjs": ">=7.8.0"
|
|
7
16
|
},
|
|
8
17
|
"dependencies": {
|
|
18
|
+
"intl-tel-input": "^25.3.2",
|
|
19
|
+
"libphonenumber-js": "^1.12.10",
|
|
9
20
|
"tslib": "^2.3.0"
|
|
10
21
|
},
|
|
11
|
-
"sideEffects": false,
|
|
12
22
|
"module": "fesm2022/ngxsmk-tel-input.mjs",
|
|
13
23
|
"typings": "index.d.ts",
|
|
14
24
|
"exports": {
|
package/public-api.d.ts
CHANGED