ngx-dial-input 2.0.2 → 2.0.3

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.
Files changed (37) hide show
  1. package/README.md +9 -12
  2. package/data/countries.d.ts +2 -0
  3. package/data/countries.model.d.ts +9 -0
  4. package/esm2020/data/countries.mjs +1218 -0
  5. package/esm2020/data/countries.model.mjs +2 -0
  6. package/esm2020/lib/phone-input.component.mjs +302 -0
  7. package/esm2020/lib/phone-input.module.mjs +24 -0
  8. package/esm2020/ngx-dial-input.mjs +5 -0
  9. package/esm2020/public-api.mjs +6 -0
  10. package/fesm2015/ngx-dial-input.mjs +1555 -0
  11. package/fesm2015/ngx-dial-input.mjs.map +1 -0
  12. package/fesm2020/ngx-dial-input.mjs +1551 -0
  13. package/fesm2020/ngx-dial-input.mjs.map +1 -0
  14. package/index.d.ts +5 -0
  15. package/lib/phone-input.component.d.ts +37 -0
  16. package/lib/phone-input.module.d.ts +9 -0
  17. package/package.json +32 -49
  18. package/{projects/phone-input/src/public-api.ts → public-api.d.ts} +0 -3
  19. package/.editorconfig +0 -16
  20. package/.vscode/extensions.json +0 -4
  21. package/.vscode/launch.json +0 -20
  22. package/.vscode/tasks.json +0 -42
  23. package/angular.json +0 -41
  24. package/projects/phone-input/README.md +0 -24
  25. package/projects/phone-input/ng-package.json +0 -7
  26. package/projects/phone-input/package.json +0 -9
  27. package/projects/phone-input/src/data/countries.model.ts +0 -9
  28. package/projects/phone-input/src/data/countries.ts +0 -1219
  29. package/projects/phone-input/src/lib/phone-input.component.css +0 -0
  30. package/projects/phone-input/src/lib/phone-input.component.html +0 -0
  31. package/projects/phone-input/src/lib/phone-input.component.ts +0 -348
  32. package/projects/phone-input/src/lib/phone-input.module.ts +0 -15
  33. package/projects/phone-input/src/lib/phone-input.service.spec.ts +0 -16
  34. package/projects/phone-input/tsconfig.lib.json +0 -15
  35. package/projects/phone-input/tsconfig.lib.prod.json +0 -10
  36. package/projects/phone-input/tsconfig.spec.json +0 -14
  37. package/tsconfig.json +0 -33
@@ -1,348 +0,0 @@
1
- import {
2
- Component,
3
- Input,
4
- Output,
5
- EventEmitter,
6
- ElementRef,
7
- HostListener,
8
- forwardRef
9
- } from '@angular/core';
10
- import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
11
- import { countries } from '../data/countries';
12
- import {
13
- trigger,
14
- state,
15
- style,
16
- transition,
17
- animate
18
- } from '@angular/animations';
19
-
20
-
21
-
22
- @Component({
23
- selector: 'phone-input',
24
- animations: [
25
- trigger('dropdownAnimation', [
26
- state('open', style({
27
- opacity: 1,
28
- transform: 'scaleY(1)',
29
- height: '*',
30
- padding: '*'
31
- })),
32
- state('closed', style({
33
- opacity: 0,
34
- transform: 'scaleY(0)',
35
- height: '0px',
36
- padding: '0px'
37
- })),
38
- transition('closed => open', [
39
- animate('200ms ease-out')
40
- ]),
41
- transition('open => closed', [
42
- animate('150ms ease-in')
43
- ])
44
- ])
45
- ],
46
- template: `<div class="phone-input-wrapper">
47
- <div class="input-container">
48
- <!-- Country Selector (20%) -->
49
- <div class="country-select" (click)="toggleDropdown()" (blur)="toggleDropdown()">
50
- <img [src]="selectedCountry.flag" width="20" />
51
- <span>{{selectedCountry.dialCode}}</span>
52
- <span class="arrow-down">▾</span>
53
- </div>
54
-
55
- <!-- Phone Input (80%) -->
56
- <input
57
- type="tel"
58
- pattern="[0-9\\s\\-]*"
59
- inputmode="numeric"
60
- class="phone-input"
61
- [placeholder]="placeholder"
62
- [(ngModel)]="phoneNumber"
63
- [ngClass]="{ 'error-border': isInvalid }"
64
- (input)="onPhoneInput()"
65
- (keypress)="allowPhoneCharacters($event)"
66
- />
67
- </div>
68
-
69
- <!-- Dropdown Content (full width below) -->
70
- <div
71
- class="dropdown-content"
72
- [@dropdownAnimation]="dropdownOpen ? 'open' : 'closed'"
73
- *ngIf="dropdownOpen || animating"
74
- (@dropdownAnimation.done)="onAnimationDone($event)" >
75
- <input
76
- *ngIf="searchEnabled"
77
- type="text"
78
- class="search-box"
79
- placeholder="Search Country"
80
- [(ngModel)]="searchTerm"
81
- (input)="onSearchChange()"
82
- />
83
- <div class="country-list">
84
- <div
85
- class="country-option"
86
- *ngFor="let country of countriesList"
87
- (click)="selectCountry(country)"
88
- >
89
- <img [src]="country.flag" width="20" />
90
- <span class="country-name">{{ country.name }} ({{ country.native }})</span>
91
- <span class="dial-code">{{ country.dialCode }}</span>
92
- </div>
93
- </div>
94
- </div>
95
- </div>
96
-
97
- `,
98
- styles: [`
99
- .phone-input-wrapper {
100
- position: relative;
101
- font-family: Arial, sans-serif;
102
- }
103
-
104
- .input-container {
105
- display: flex;
106
- width: 100%;
107
- padding-bottom: 3px;
108
- }
109
-
110
- /* Country Select (20%) */
111
- .country-select {
112
- width: 24%;
113
- display: flex;
114
- align-items: center;
115
- justify-content: space-between;
116
- border: 1px solid #ccc;
117
- border-right: none;
118
- border-radius: 4px 0 0 4px;
119
- padding: 8px 8px;
120
- cursor: pointer;
121
- background-color: #f8f8f8;
122
- }
123
-
124
- .country-select img {
125
- margin-right: 5px;
126
- }
127
-
128
- .arrow-down {
129
- font-size: 13px;
130
- color: #666;
131
- }
132
- .arrow-down:after {
133
- display: none;
134
- }
135
- /* Phone Input (80%) */
136
- .phone-input {
137
- width: 80%;
138
- padding: 8px 12px;
139
- border: 1px solid #ccc;
140
- border-radius: 0 4px 4px 0;
141
- outline: none;
142
- }
143
-
144
- /* Dropdown Content */
145
- .dropdown-content {
146
- position: absolute;
147
- top: 100%;
148
- left: 0;
149
- width: 100%;
150
- background: white;
151
- border: 1px solid #ccc;
152
- border-radius: 0 0 4px 4px;
153
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
154
- z-index: 1000;
155
- margin-top: -1px;
156
- }
157
-
158
- .search-box {
159
- width: 100%;
160
- padding: 8px 12px;
161
- border: none;
162
- border-bottom: 1px solid #eee;
163
- outline: none;
164
- }
165
-
166
- .country-list {
167
- max-height: 200px;
168
- overflow-y: auto;
169
- }
170
-
171
- .country-option {
172
- display: flex;
173
- align-items: center;
174
- padding: 8px 12px;
175
- cursor: pointer;
176
- }
177
-
178
- .country-option:hover {
179
- background-color: #f5f5f5;
180
- }
181
-
182
- .country-option img {
183
- margin-right: 5px;
184
- }
185
-
186
- .country-name {
187
- flex-grow: 1;
188
- }
189
-
190
- .dial-code {
191
- color: #666;
192
- font-size: 0.9em;
193
- }
194
-
195
- /* Error State */
196
- input.error-border {
197
- border-color: red;
198
- }
199
-
200
- `],
201
- providers: [{
202
- provide: NG_VALUE_ACCESSOR,
203
- useExisting: forwardRef(() => PhoneInputComponent),
204
- multi: true
205
- }]
206
- })
207
- export class PhoneInputComponent implements ControlValueAccessor {
208
- countriesList = countries;
209
- searchTerm = '';
210
- @Input() defaultCountry: string = '';
211
- @Input() placeholder: string = '';
212
- @Input() searchEnabled: boolean = true;
213
- @Input() numberFormat: 'international' | 'national' = 'international';
214
- @Input() setFirstCountry: string = '';
215
- @Output() phoneChange = new EventEmitter<any>();
216
-
217
- phoneNumber: string = '';
218
- isInvalid: boolean = false;
219
- dropdownOpen: boolean = false;
220
- animating: boolean = false;
221
-
222
- selectedCountry = countries[0];
223
-
224
- constructor(private eRef: ElementRef) {}
225
-
226
- private onChange: any = () => {};
227
- private onTouched: any = () => {};
228
-
229
-
230
- ngOnInit() {
231
- if (this.defaultCountry) {
232
- const match = countries.find(c => c.iso2 === this.defaultCountry.toLowerCase());
233
- if (match) this.selectedCountry = match;
234
- }
235
- }
236
- @HostListener('document:click', ['$event'])
237
- handleClickOutside(event: MouseEvent) {
238
- if (!this.eRef.nativeElement.contains(event.target)) {
239
- this.dropdownOpen = false;
240
- }
241
- }
242
-
243
- onAnimationDone(event: any) {
244
- if (!this.dropdownOpen) {
245
- this.animating = false;
246
- }
247
- }
248
-
249
- toggleDropdown() {
250
- this.dropdownOpen = !this.dropdownOpen;
251
- this.animating = true;
252
- }
253
-
254
- onClickOutside(event: MouseEvent) {
255
- if (!this.eRef.nativeElement.contains(event.target)) {
256
- this.dropdownOpen = false;
257
- }
258
- }
259
-
260
- onSearchChange() {
261
- const term = this.searchTerm.toLowerCase();
262
- this.countriesList = countries.filter(c =>
263
- c.name.toLowerCase().includes(term) ||
264
- (c.native?.toLowerCase().includes(term))
265
- );
266
- }
267
-
268
-
269
- selectCountry(country: any) {
270
- this.selectedCountry = country;
271
- this.searchTerm = '';
272
- this.countriesList = countries;
273
- this.dropdownOpen = false;
274
- this.emitPhoneChange();
275
- }
276
-
277
- onPhoneInput() {
278
- this.emitPhoneChange();
279
- }
280
-
281
- private emitPhoneChange() {
282
- const raw = this.phoneNumber || '';
283
- const digitsOnly = raw.replace(/\D/g, '');
284
- const hyphenCount = (raw.match(/-/g) || []).length;
285
- const spaceCount = (raw.match(/ /g) || []).length;
286
-
287
- const totalChars = this.phoneNumber.length;
288
- const maxAllowedLength = this.selectedCountry?.phoneLength + hyphenCount + spaceCount;
289
-
290
- if (digitsOnly.length !== this.selectedCountry?.phoneLength) {
291
- this.isInvalid = true;
292
- }
293
- else if (totalChars > maxAllowedLength) {
294
- this.isInvalid = true;
295
- }
296
- else {
297
- this.isInvalid = false;
298
- }
299
- const fullPhone = this.selectedCountry.dialCode + raw;
300
-
301
- const structuredValue = {
302
- e164Number: fullPhone,
303
- nationalNumber: raw,
304
- dialCode: this.selectedCountry.dialCode,
305
- countryCode: this.selectedCountry.iso2,
306
- isValid: !this.isInvalid,
307
- };
308
-
309
- this.onChange(structuredValue);
310
- this.phoneChange.emit(structuredValue);
311
- }
312
-
313
- writeValue(value: any): void {
314
- if (value && value.dialCode && value.countryCode) {
315
- const match = countries.find(c =>
316
- c.dialCode === value.dialCode && c.iso2 === value.countryCode
317
- );
318
- if (match) {
319
- this.selectedCountry = match;
320
- } else {
321
- this.selectedCountry = countries[0];
322
- }
323
- this.phoneNumber = value.nationalNumber || '';
324
- } else {
325
- this.selectedCountry = countries[0];
326
- this.phoneNumber = '';
327
- }
328
- }
329
- registerOnChange(fn: any): void {
330
- this.onChange = fn;
331
- }
332
-
333
- registerOnTouched(fn: any): void {
334
- this.onTouched = fn;
335
- }
336
- allowPhoneCharacters(event: KeyboardEvent) {
337
- const allowedChars = [' ', '-'];
338
- const charCode = event.charCode;
339
-
340
- // Allow digits 0–9
341
- if (charCode >= 48 && charCode <= 57) return;
342
-
343
- // Allow hyphen and space
344
- if (allowedChars.includes(event.key)) return;
345
-
346
- event.preventDefault();
347
- }
348
- }
@@ -1,15 +0,0 @@
1
- import { NgModule } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { PhoneInputComponent } from './phone-input.component';
4
- import { FormsModule } from '@angular/forms';
5
-
6
- @NgModule({
7
- declarations: [PhoneInputComponent],
8
- imports: [
9
- CommonModule,
10
- FormsModule
11
- ],
12
- exports: [PhoneInputComponent]
13
- })
14
- export class PhoneInputModule { }
15
-
@@ -1,16 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
-
3
- import { PhoneInputService } from './phone-input.service';
4
-
5
- describe('PhoneInputService', () => {
6
- let service: PhoneInputService;
7
-
8
- beforeEach(() => {
9
- TestBed.configureTestingModule({});
10
- service = TestBed.inject(PhoneInputService);
11
- });
12
-
13
- it('should be created', () => {
14
- expect(service).toBeTruthy();
15
- });
16
- });
@@ -1,15 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "../../out-tsc/lib",
5
- "rootDir": "src",
6
- "declaration": true,
7
- "declarationMap": true,
8
- "inlineSources": true,
9
- "types": [],
10
- "composite": false
11
- },
12
- "exclude": [
13
- "**/*.spec.ts"
14
- ]
15
- }
@@ -1,10 +0,0 @@
1
- /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
- {
3
- "extends": "./tsconfig.lib.json",
4
- "compilerOptions": {
5
- "declarationMap": false
6
- },
7
- "angularCompilerOptions": {
8
- "compilationMode": "partial"
9
- }
10
- }
@@ -1,14 +0,0 @@
1
- /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
- {
3
- "extends": "../../tsconfig.json",
4
- "compilerOptions": {
5
- "outDir": "../../out-tsc/spec",
6
- "types": [
7
- "jasmine"
8
- ]
9
- },
10
- "include": [
11
- "**/*.spec.ts",
12
- "**/*.d.ts"
13
- ]
14
- }
package/tsconfig.json DELETED
@@ -1,33 +0,0 @@
1
- /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
- {
3
- "compileOnSave": false,
4
- "compilerOptions": {
5
- "baseUrl": "./",
6
- "outDir": "./dist/out-tsc",
7
- "forceConsistentCasingInFileNames": true,
8
- "strict": true,
9
- "noImplicitOverride": true,
10
- "noPropertyAccessFromIndexSignature": true,
11
- "noImplicitReturns": true,
12
- "noFallthroughCasesInSwitch": true,
13
- "skipLibCheck": true,
14
- "esModuleInterop": true,
15
- "sourceMap": true,
16
- "declaration": false,
17
- "experimentalDecorators": true,
18
- "moduleResolution": "node",
19
- "importHelpers": true,
20
- "module": "es2020",
21
- "target": "es2015",
22
- "useDefineForClassFields": false,
23
- "lib": ["es2020", "dom"]
24
- },
25
- "angularCompilerOptions": {
26
- "enableI18nLegacyMessageIdFormat": false,
27
- "strictInjectionParameters": true,
28
- "strictInputAccessModifiers": true,
29
- "strictTemplates": true,
30
- "compilationMode": "partial"
31
-
32
- }
33
- }