ngxsmk-tel-input 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,9 +10,9 @@ Wraps [`intl-tel-input`](https://github.com/jackocnr/intl-tel-input) for the UI
10
10
  ## Screenshots
11
11
 
12
12
  <p align="left">
13
- <img src="https://unpkg.com/ngxsmk-tel-input@1.0.4/docs/valid.png" alt="Angular international phone input - valid" width="420" />
13
+ <img src="https://unpkg.com/ngxsmk-tel-input@latest/docs/valid.png" alt="Angular international phone input - valid" width="420" />
14
14
  &nbsp;&nbsp;
15
- <img src="https://unpkg.com/ngxsmk-tel-input@1.0.4/docs/invalid.png" alt="Angular international phone input - Invalid" width="420" />
15
+ <img src="https://unpkg.com/ngxsmk-tel-input@latest/docs/invalid.png" alt="Angular international phone input - Invalid" width="420" />
16
16
  </p>
17
17
 
18
18
  ---
@@ -157,7 +157,7 @@ export class AppComponent {
157
157
 
158
158
  You can localize the dropdown/search labels and override country names.
159
159
 
160
- <img src="https://unpkg.com/ngxsmk-tel-input@1.0.4/docs/kr.png" alt="Angular international phone input - Korean Localization & RTL" width="420" />
160
+ <img src="https://unpkg.com/ngxsmk-tel-input@latest/docs/kr.png" alt="Angular international phone input - Korean Localization & RTL" width="420" />
161
161
 
162
162
  Korean example
163
163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngxsmk-tel-input",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "Angular international telephone input with country flag dropdown, formatting & validation (intl-tel-input + libphonenumber). ControlValueAccessor. Supports Angular 17–19.",
5
5
  "keywords": [
6
6
  "angular",
@@ -14,7 +14,7 @@ import {
14
14
  SimpleChanges,
15
15
  ViewChild
16
16
  } from '@angular/core';
17
- import { isPlatformBrowser } from '@angular/common';
17
+ import {isPlatformBrowser} from '@angular/common';
18
18
  import {
19
19
  AbstractControl,
20
20
  ControlValueAccessor,
@@ -23,8 +23,8 @@ import {
23
23
  ValidationErrors,
24
24
  Validator
25
25
  } from '@angular/forms';
26
- import type { CountryCode } from 'libphonenumber-js';
27
- import { NgxsmkTelInputService } from './ngxsmk-tel-input.service';
26
+ import type {CountryCode} from 'libphonenumber-js';
27
+ import {NgxsmkTelInputService} from './ngxsmk-tel-input.service';
28
28
 
29
29
  type IntlTelInstance = any;
30
30
  export type CountryMap = Partial<Record<CountryCode, string>>;
@@ -90,8 +90,8 @@ export interface IntlTelI18n {
90
90
  </div>
91
91
  `,
92
92
  providers: [
93
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true },
94
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true }
93
+ {provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true},
94
+ {provide: NG_VALIDATORS, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true}
95
95
  ],
96
96
  styles: [`
97
97
  /* ---------- Theme tokens ---------- */
@@ -131,37 +131,87 @@ export interface IntlTelI18n {
131
131
  }
132
132
 
133
133
  /* ---------- Structure ---------- */
134
- .ngxsmk-tel { width: 100%; color: var(--tel-fg); }
135
- .ngxsmk-tel.disabled { opacity: .7; cursor: not-allowed; }
134
+ .ngxsmk-tel {
135
+ width: 100%;
136
+ color: var(--tel-fg);
137
+ }
138
+
139
+ .ngxsmk-tel.disabled {
140
+ opacity: .7;
141
+ cursor: not-allowed;
142
+ }
136
143
 
137
- .ngxsmk-tel__label { display: inline-block; margin-bottom: 6px; font-size: .875rem; font-weight: 500; }
138
- .ngxsmk-tel__wrap { position: relative; }
139
- .ngxsmk-tel-input__wrapper, :host ::ng-deep .iti { width: 100%; }
144
+ .ngxsmk-tel__label {
145
+ display: inline-block;
146
+ margin-bottom: 6px;
147
+ font-size: .875rem;
148
+ font-weight: 500;
149
+ }
150
+
151
+ .ngxsmk-tel__wrap {
152
+ position: relative;
153
+ }
154
+
155
+ .ngxsmk-tel-input__wrapper, :host ::ng-deep .iti {
156
+ width: 100%;
157
+ }
140
158
 
141
159
  .ngxsmk-tel-input__control {
142
- width: 100%; height: 40px; font: inherit; color: var(--tel-fg);
143
- background: var(--tel-bg); border: 1px solid var(--tel-border);
160
+ width: 100%;
161
+ height: 40px;
162
+ font: inherit;
163
+ color: var(--tel-fg);
164
+ background: var(--tel-bg);
165
+ border: 1px solid var(--tel-border);
144
166
  border-radius: var(--tel-radius);
145
167
  padding: 10px 40px 10px 12px;
146
168
  outline: none;
147
169
  transition: border-color .15s, box-shadow .15s, background .15s;
148
170
  }
149
- .ngxsmk-tel-input__control::placeholder { color: var(--tel-placeholder); }
150
- .ngxsmk-tel-input__control:hover { border-color: var(--tel-border-hover); }
151
- .ngxsmk-tel-input__control:focus { border-color: var(--tel-ring); box-shadow: var(--tel-focus-shadow); }
171
+
172
+ .ngxsmk-tel-input__control::placeholder {
173
+ color: var(--tel-placeholder);
174
+ }
175
+
176
+ .ngxsmk-tel-input__control:hover {
177
+ border-color: var(--tel-border-hover);
178
+ }
179
+
180
+ .ngxsmk-tel-input__control:focus {
181
+ border-color: var(--tel-ring);
182
+ box-shadow: var(--tel-focus-shadow);
183
+ }
152
184
 
153
185
  [data-size="sm"] .ngxsmk-tel-input__control {
154
- height: 34px; font-size: 13px; padding: 6px 36px 6px 10px; border-radius: 10px;
186
+ height: 34px;
187
+ font-size: 13px;
188
+ padding: 6px 36px 6px 10px;
189
+ border-radius: 10px;
155
190
  }
191
+
156
192
  [data-size="lg"] .ngxsmk-tel-input__control {
157
- height: 46px; font-size: 16px; padding: 12px 44px 12px 14px; border-radius: 14px;
193
+ height: 46px;
194
+ font-size: 16px;
195
+ padding: 12px 44px 12px 14px;
196
+ border-radius: 14px;
197
+ }
198
+
199
+ [data-variant="filled"] .ngxsmk-tel-input__control {
200
+ background: rgba(148, 163, 184, .08);
158
201
  }
159
202
 
160
- [data-variant="filled"] .ngxsmk-tel-input__control { background: rgba(148, 163, 184, .08); }
161
203
  [data-variant="underline"] .ngxsmk-tel-input__control {
162
- border: 0; border-bottom: 2px solid var(--tel-border); border-radius: 0; padding-left: 0; padding-right: 34px;
204
+ border: 0;
205
+ border-bottom: 2px solid var(--tel-border);
206
+ border-radius: 0;
207
+ padding-left: 0;
208
+ padding-right: 34px;
209
+ }
210
+
211
+ [data-variant="underline"] .ngxsmk-tel-input__control:focus {
212
+ border-bottom-color: var(--tel-ring);
213
+ box-shadow: none;
163
214
  }
164
- [data-variant="underline"] .ngxsmk-tel-input__control:focus { border-bottom-color: var(--tel-ring); box-shadow: none; }
165
215
 
166
216
  /* ---------- intl-tel-input dropdown (deep selectors) ---------- */
167
217
  :host ::ng-deep .iti__flag-container {
@@ -171,37 +221,89 @@ export interface IntlTelI18n {
171
221
  border-right: none;
172
222
  background: var(--tel-bg);
173
223
  }
174
- :host ::ng-deep .iti__selected-flag { height: 100%; padding: 0 10px; display: inline-flex; align-items: center; }
224
+
225
+ :host ::ng-deep .iti__selected-flag {
226
+ height: 100%;
227
+ padding: 0 10px;
228
+ display: inline-flex;
229
+ align-items: center;
230
+ }
175
231
 
176
232
  :host ::ng-deep .iti__country-list {
177
- background: var(--tel-dd-bg); border: 1px solid var(--tel-dd-border);
178
- border-radius: var(--tel-dd-radius); box-shadow: var(--tel-dd-shadow);
179
- max-height: min(50vh, 360px); overflow: auto; padding: 6px 0;
180
- width: max(280px, 100%); z-index: var(--tel-dd-z);
233
+ background: var(--tel-dd-bg);
234
+ border: 1px solid var(--tel-dd-border);
235
+ border-radius: var(--tel-dd-radius);
236
+ box-shadow: var(--tel-dd-shadow);
237
+ max-height: min(50vh, 360px);
238
+ overflow: auto;
239
+ padding: 6px 0;
240
+ width: max(280px, 100%);
241
+ z-index: var(--tel-dd-z);
242
+ }
243
+
244
+ :host ::ng-deep .iti--container .iti__country-list {
245
+ z-index: var(--tel-dd-z);
181
246
  }
182
- :host ::ng-deep .iti--container .iti__country-list { z-index: var(--tel-dd-z); }
183
247
 
184
248
  :host ::ng-deep .iti__search-input {
185
- position: sticky; top: 0; margin: 0; padding: 10px 12px; width: 100%;
186
- border: 0; border-bottom: 1px solid var(--tel-dd-border); outline: none;
187
- background: var(--tel-dd-search-bg); color: var(--tel-fg);
249
+ position: sticky;
250
+ top: 0;
251
+ margin: 0;
252
+ padding: 10px 12px;
253
+ width: 100%;
254
+ border: 0;
255
+ border-bottom: 1px solid var(--tel-dd-border);
256
+ outline: none;
257
+ background: var(--tel-dd-search-bg);
258
+ color: var(--tel-fg);
188
259
  }
189
260
 
190
261
  :host ::ng-deep .iti__country {
191
- display: grid; grid-template-columns: 28px 1fr auto; align-items: center;
192
- column-gap: .5rem; padding: 10px 12px; cursor: pointer;
262
+ display: grid;
263
+ grid-template-columns: 28px 1fr auto;
264
+ align-items: center;
265
+ column-gap: .5rem;
266
+ padding: 10px 12px;
267
+ cursor: pointer;
268
+ }
269
+
270
+ :host ::ng-deep .iti__dial-code {
271
+ color: var(--tel-placeholder);
272
+ font-weight: 600;
273
+ margin-left: 10px;
193
274
  }
194
- :host ::ng-deep .iti__dial-code { color: var(--tel-placeholder); font-weight: 600; margin-left: 10px; }
195
275
 
196
276
  .ngxsmk-tel__clear {
197
- position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
198
- border: 0; background: transparent; font-size: 18px; line-height: 1;
199
- width: 28px; height: 28px; border-radius: 50%; cursor: pointer; color: var(--tel-placeholder);
277
+ position: absolute;
278
+ right: 8px;
279
+ top: 50%;
280
+ transform: translateY(-50%);
281
+ border: 0;
282
+ background: transparent;
283
+ font-size: 18px;
284
+ line-height: 1;
285
+ width: 28px;
286
+ height: 28px;
287
+ border-radius: 50%;
288
+ cursor: pointer;
289
+ color: var(--tel-placeholder);
290
+ }
291
+
292
+ .ngxsmk-tel__clear:hover {
293
+ background: rgba(148, 163, 184, .15);
294
+ }
295
+
296
+ .ngxsmk-tel__hint {
297
+ margin-top: 6px;
298
+ font-size: 12px;
299
+ color: var(--tel-placeholder);
200
300
  }
201
- .ngxsmk-tel__clear:hover { background: rgba(148, 163, 184, .15); }
202
301
 
203
- .ngxsmk-tel__hint { margin-top: 6px; font-size: 12px; color: var(--tel-placeholder); }
204
- .ngxsmk-tel__error { margin-top: 6px; font-size: 12px; color: var(--tel-error); }
302
+ .ngxsmk-tel__error {
303
+ margin-top: 6px;
304
+ font-size: 12px;
305
+ color: var(--tel-error);
306
+ }
205
307
 
206
308
  .ngxsmk-tel__wrap.has-error .ngxsmk-tel-input__control {
207
309
  border-color: var(--tel-error);
@@ -210,7 +312,7 @@ export interface IntlTelI18n {
210
312
  `]
211
313
  })
212
314
  export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor, Validator {
213
- @ViewChild('telInput', { static: true }) inputRef!: ElementRef<HTMLInputElement>;
315
+ @ViewChild('telInput', {static: true}) inputRef!: ElementRef<HTMLInputElement>;
214
316
 
215
317
  /* Core config */
216
318
  @Input() initialCountry: CountryCode | 'auto' = 'US';
@@ -244,9 +346,17 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
244
346
 
245
347
  /* Localization + RTL */
246
348
  @Input('i18n') i18n?: IntlTelI18n;
247
- @Input('telI18n') set telI18n(v: IntlTelI18n | undefined) { this.i18n = v; }
349
+
350
+ @Input('telI18n') set telI18n(v: IntlTelI18n | undefined) {
351
+ this.i18n = v;
352
+ }
353
+
248
354
  @Input('localizedCountries') localizedCountries?: CountryMap;
249
- @Input('telLocalizedCountries') set telLocalizedCountries(v: CountryMap | undefined) { this.localizedCountries = v; }
355
+
356
+ @Input('telLocalizedCountries') set telLocalizedCountries(v: CountryMap | undefined) {
357
+ this.localizedCountries = v;
358
+ }
359
+
250
360
  @Input() clearAriaLabel = 'Clear phone number';
251
361
  @Input() dir: 'ltr' | 'rtl' = 'ltr';
252
362
 
@@ -266,8 +376,10 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
266
376
 
267
377
  /* Internal */
268
378
  private iti: IntlTelInstance | null = null;
269
- private onChange: (val: string | null) => void = () => {};
270
- private onTouchedCb: () => void = () => {};
379
+ private onChange: (val: string | null) => void = () => {
380
+ };
381
+ private onTouchedCb: () => void = () => {
382
+ };
271
383
  private validatorChange?: () => void;
272
384
  private lastEmittedValid = false;
273
385
  private pendingWrite: string | null = null;
@@ -279,7 +391,8 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
279
391
  private readonly zone: NgZone,
280
392
  private readonly tel: NgxsmkTelInputService,
281
393
  @Inject(PLATFORM_ID) private readonly platformId: Object
282
- ) {}
394
+ ) {
395
+ }
283
396
 
284
397
  async ngAfterViewInit() {
285
398
  if (!isPlatformBrowser(this.platformId)) return;
@@ -297,11 +410,11 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
297
410
  ngOnChanges(changes: SimpleChanges): void {
298
411
  if (!isPlatformBrowser(this.platformId)) return;
299
412
  const configChanged = [
300
- 'initialCountry','preferredCountries','onlyCountries',
301
- 'separateDialCode','allowDropdown','nationalMode',
302
- 'i18n','localizedCountries','dir',
303
- 'autoPlaceholder','utilsScript','customPlaceholder',
304
- 'digitsOnly','allowLeadingPlus'
413
+ 'initialCountry', 'preferredCountries', 'onlyCountries',
414
+ 'separateDialCode', 'allowDropdown', 'nationalMode',
415
+ 'i18n', 'localizedCountries', 'dir',
416
+ 'autoPlaceholder', 'utilsScript', 'customPlaceholder',
417
+ 'digitsOnly', 'allowLeadingPlus'
305
418
  ].some(k => k in changes && !changes[k]?.firstChange);
306
419
  if (configChanged && this.iti) {
307
420
  this.reinitPlugin();
@@ -322,8 +435,15 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
322
435
  }
323
436
  this.setInputValue(val ?? '');
324
437
  }
325
- registerOnChange(fn: any): void { this.onChange = fn; }
326
- registerOnTouched(fn: any): void { this.onTouchedCb = fn; }
438
+
439
+ registerOnChange(fn: any): void {
440
+ this.onChange = fn;
441
+ }
442
+
443
+ registerOnTouched(fn: any): void {
444
+ this.onTouchedCb = fn;
445
+ }
446
+
327
447
  setDisabledState(isDisabled: boolean): void {
328
448
  this.disabled = isDisabled;
329
449
  if (this.inputRef) this.inputRef.nativeElement.disabled = isDisabled;
@@ -338,9 +458,12 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
338
458
  this.lastEmittedValid = valid;
339
459
  this.validityChange.emit(valid);
340
460
  }
341
- return valid ? null : { phoneInvalid: true };
461
+ return valid ? null : {phoneInvalid: true};
462
+ }
463
+
464
+ registerOnValidatorChange(fn: () => void): void {
465
+ this.validatorChange = fn;
342
466
  }
343
- registerOnValidatorChange(fn: () => void): void { this.validatorChange = fn; }
344
467
 
345
468
  // ----- Public helpers -----
346
469
  focus(): void {
@@ -350,12 +473,14 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
350
473
  queueMicrotask(() => el.setSelectionRange(0, el.value.length));
351
474
  }
352
475
  }
476
+
353
477
  selectCountry(iso2: CountryCode): void {
354
478
  if (this.iti) {
355
479
  this.iti.setCountry(iso2.toLowerCase());
356
480
  this.handleInput();
357
481
  }
358
482
  }
483
+
359
484
  clearInput() {
360
485
  this.setInputValue('');
361
486
  this.handleInput();
@@ -364,7 +489,7 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
364
489
 
365
490
  // ----- Plugin wiring -----
366
491
  private async initIntlTelInput() {
367
- const [{ default: intlTelInput }] = await Promise.all([import('intl-tel-input')]);
492
+ const [{default: intlTelInput}] = await Promise.all([import('intl-tel-input')]);
368
493
 
369
494
  const toLowerKeys = (m?: CountryMap) =>
370
495
  m ? Object.fromEntries(Object.entries(m).map(([k, v]) => [k.toLowerCase(), v])) : undefined;
@@ -485,7 +610,7 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
485
610
  el.addEventListener('countrychange', () => {
486
611
  const iso2 = this.currentIso2();
487
612
  this.zone.run(() => {
488
- this.countryChange.emit({ iso2 });
613
+ this.countryChange.emit({iso2});
489
614
  this.validatorChange?.();
490
615
  });
491
616
  this.handleInput();
@@ -519,7 +644,7 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
519
644
  const iso2 = this.currentIso2();
520
645
  const parsed = this.tel.parse(raw, iso2);
521
646
  this.zone.run(() => this.onChange(parsed.e164)); // E.164 or null
522
- this.zone.run(() => this.inputChange.emit({ raw, e164: parsed.e164, iso2 }));
647
+ this.zone.run(() => this.inputChange.emit({raw, e164: parsed.e164, iso2}));
523
648
  if (raw && this.nationalMode && parsed.national) {
524
649
  const normalized = parsed.national.replace(/\s{2,}/g, ' ');
525
650
  if (normalized !== raw) this.setInputValue(normalized);