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 { 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';
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
- 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 ?? {}) };
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
- /** Update defaults at runtime if you need to (multi-tenant apps, theming, etc.) */
17
- setDefaults(partial) {
18
- this.defaults = { ...this.defaults, ...partial };
15
+ isValid(input, iso2) {
16
+ const phone = parsePhoneNumberFromString(input, iso2);
17
+ return !!phone && phone.isValid();
19
18
  }
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' });
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: ngxsmkTelInputService, decorators: [{
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
- }], ctorParameters: () => [{ type: undefined, decorators: [{
84
- type: Optional
85
- }, {
86
- type: Inject,
87
- args: [ngxsmk_TEL_DEFAULTS]
88
- }] }] });
25
+ }] });
89
26
 
90
- class ngxsmkTelInputComponent {
27
+ class NgxsmkTelInputComponent {
91
28
  zone;
92
29
  tel;
93
30
  platformId;
94
31
  inputRef;
95
- // ===== Inputs (public API) =====
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
- // ===== Outputs =====
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
- // ===== Internal =====
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; // cache writeValue before plugin ready
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
- this.destroyPlugin();
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
- // ========== Validator ==========
117
+ // ----- Validator -----
181
118
  validate(_) {
182
119
  const raw = this.currentRaw();
183
120
  if (!raw)
184
- return null; // let "required" handle empties
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
- // ========== Public Helpers ==========
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
- markTouched() {
203
- this.onTouched();
143
+ clearInput() {
144
+ this.setInputValue('');
145
+ this.handleInput();
146
+ this.inputRef.nativeElement.focus();
204
147
  }
205
- // ========== Private: DOM & Plugin ==========
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 // keep bundle small; we use libphonenumber-js via the service
159
+ utilsScript: undefined,
160
+ dropdownContainer: this.dropdownAttachToBody && typeof document !== 'undefined' ? document.body : undefined
221
161
  };
222
- this.zone.runOutsideAngular(() => {
223
- this.iti = intlTelInput(this.inputRef.nativeElement, config);
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.zone.run(() => this.onTouched()));
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
- // 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.
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
- this.inputRef.nativeElement.value = v ?? '';
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: 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 }
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="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
- />
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: ngxsmkTelInputComponent, decorators: [{
281
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NgxsmkTelInputComponent, decorators: [{
310
282
  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
- />
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
- 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: [{
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 { ngxsmkTelInputComponent, ngxsmkTelInputService, ngxsmk_TEL_DEFAULTS };
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 { ngxsmkTelInputService } from './ngxsmk-tel-input.service';
4
+ import { NgxsmkTelInputService } from './ngxsmk-tel-input.service';
5
5
  import * as i0 from "@angular/core";
6
- export declare class ngxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor {
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 onTouched;
46
+ private onTouchedCb;
37
47
  private lastEmittedValid;
38
48
  private pendingWrite;
39
- constructor(zone: NgZone, tel: ngxsmkTelInputService, platformId: Object);
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
- markTouched(): void;
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
- private currentRaw;
70
+ currentRaw(): string;
57
71
  private currentIso2;
58
72
  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>;
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 { InjectionToken } from '@angular/core';
2
- import { CountryCode, PhoneNumber } from 'libphonenumber-js';
1
+ import { type CountryCode } from 'libphonenumber-js';
3
2
  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>;
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",
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/common": "^19.2.0",
6
- "@angular/core": "^19.2.0"
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
@@ -1,2 +1,2 @@
1
- export * from './lib/ngxsmk-tel-input.service';
2
1
  export * from './lib/ngxsmk-tel-input.component';
2
+ export * from './lib/ngxsmk-tel-input.service';