ngxsmk-tel-input 1.1.0 → 1.1.2
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/package.json
CHANGED
|
@@ -1,20 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngxsmk-tel-input",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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
|
+
"ngxsmk-tel-input",
|
|
7
|
+
"ngx",
|
|
6
8
|
"angular",
|
|
9
|
+
"angular17",
|
|
10
|
+
"angular18",
|
|
11
|
+
"angular19",
|
|
7
12
|
"angular-forms",
|
|
13
|
+
"reactive-forms",
|
|
14
|
+
"template-driven-forms",
|
|
8
15
|
"controlvalueaccessor",
|
|
16
|
+
"cva",
|
|
9
17
|
"phone-input",
|
|
10
|
-
"telephone",
|
|
11
|
-
"
|
|
12
|
-
"international",
|
|
18
|
+
"telephone-input",
|
|
19
|
+
"phone-number",
|
|
20
|
+
"international-phone",
|
|
13
21
|
"intl-tel-input",
|
|
14
22
|
"libphonenumber",
|
|
23
|
+
"libphonenumber-js",
|
|
15
24
|
"e164",
|
|
25
|
+
"phone-validation",
|
|
16
26
|
"phone-number-validation",
|
|
17
|
-
"country-
|
|
27
|
+
"country-code",
|
|
28
|
+
"dial-code",
|
|
29
|
+
"separate-dial-code",
|
|
30
|
+
"national-mode",
|
|
31
|
+
"country-flags",
|
|
32
|
+
"dropdown",
|
|
33
|
+
"i18n",
|
|
34
|
+
"localization",
|
|
35
|
+
"rtl",
|
|
36
|
+
"a11y",
|
|
37
|
+
"accessibility",
|
|
38
|
+
"ssr",
|
|
39
|
+
"angular-universal",
|
|
40
|
+
"typescript"
|
|
18
41
|
],
|
|
19
42
|
"homepage": "https://github.com/toozuuu/ngxsmk-tel-input#readme",
|
|
20
43
|
"repository": {
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/* ---------- Theme tokens ---------- */
|
|
2
|
+
:host {
|
|
3
|
+
--tel-bg: #fff;
|
|
4
|
+
--tel-fg: #0f172a;
|
|
5
|
+
--tel-border: #c0c0c0;
|
|
6
|
+
--tel-border-hover: #9aa0a6;
|
|
7
|
+
--tel-ring: #2563eb;
|
|
8
|
+
--tel-placeholder: #9ca3af;
|
|
9
|
+
--tel-error: #ef4444;
|
|
10
|
+
--tel-radius: 12px;
|
|
11
|
+
--tel-focus-shadow: 0 0 0 3px rgba(37, 99, 235, .25);
|
|
12
|
+
|
|
13
|
+
--tel-dd-bg: var(--tel-bg);
|
|
14
|
+
--tel-dd-border: var(--tel-border);
|
|
15
|
+
--tel-dd-shadow: 0 24px 60px rgba(0, 0, 0, .18);
|
|
16
|
+
--tel-dd-radius: 12px;
|
|
17
|
+
--tel-dd-item-hover: rgba(37, 99, 235, .08);
|
|
18
|
+
--tel-dd-z: 2000;
|
|
19
|
+
--tel-dd-search-bg: rgba(148, 163, 184, .08);
|
|
20
|
+
|
|
21
|
+
display: block;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:host-context(.dark) {
|
|
25
|
+
--tel-bg: #0b0f17;
|
|
26
|
+
--tel-fg: #e5e7eb;
|
|
27
|
+
--tel-border: #334155;
|
|
28
|
+
--tel-border-hover: #475569;
|
|
29
|
+
--tel-ring: #60a5fa;
|
|
30
|
+
--tel-placeholder: #94a3b8;
|
|
31
|
+
|
|
32
|
+
--tel-dd-bg: #0f1521;
|
|
33
|
+
--tel-dd-border: #324056;
|
|
34
|
+
--tel-dd-search-bg: rgba(148, 163, 184, .12);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ---------- Structure ---------- */
|
|
38
|
+
.ngxsmk-tel {
|
|
39
|
+
width: 100%;
|
|
40
|
+
color: var(--tel-fg);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ngxsmk-tel.disabled {
|
|
44
|
+
opacity: .7;
|
|
45
|
+
cursor: not-allowed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ngxsmk-tel__label {
|
|
49
|
+
display: inline-block;
|
|
50
|
+
margin-bottom: 6px;
|
|
51
|
+
font-size: .875rem;
|
|
52
|
+
font-weight: 500;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.ngxsmk-tel__wrap {
|
|
56
|
+
position: relative;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ngxsmk-tel-input__wrapper,
|
|
60
|
+
:host ::ng-deep .iti {
|
|
61
|
+
width: 100%;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.ngxsmk-tel-input__control {
|
|
65
|
+
width: 100%;
|
|
66
|
+
height: 40px;
|
|
67
|
+
font: inherit;
|
|
68
|
+
color: var(--tel-fg);
|
|
69
|
+
background: var(--tel-bg);
|
|
70
|
+
border: 1px solid var(--tel-border);
|
|
71
|
+
border-radius: var(--tel-radius);
|
|
72
|
+
padding: 10px 40px 10px 12px;
|
|
73
|
+
outline: none;
|
|
74
|
+
transition: border-color .15s, box-shadow .15s, background .15s;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.ngxsmk-tel-input__control::placeholder {
|
|
78
|
+
color: var(--tel-placeholder);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.ngxsmk-tel-input__control:hover {
|
|
82
|
+
border-color: var(--tel-border-hover);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ngxsmk-tel-input__control:focus {
|
|
86
|
+
border-color: var(--tel-ring);
|
|
87
|
+
box-shadow: var(--tel-focus-shadow);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Size presets */
|
|
91
|
+
[data-size="sm"] .ngxsmk-tel-input__control {
|
|
92
|
+
height: 34px;
|
|
93
|
+
font-size: 13px;
|
|
94
|
+
padding: 6px 36px 6px 10px;
|
|
95
|
+
border-radius: 10px;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
[data-size="lg"] .ngxsmk-tel-input__control {
|
|
99
|
+
height: 46px;
|
|
100
|
+
font-size: 16px;
|
|
101
|
+
padding: 12px 44px 12px 14px;
|
|
102
|
+
border-radius: 14px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Variants */
|
|
106
|
+
[data-variant="filled"] .ngxsmk-tel-input__control {
|
|
107
|
+
background: rgba(148, 163, 184, .08);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
[data-variant="underline"] .ngxsmk-tel-input__control {
|
|
111
|
+
border: 0;
|
|
112
|
+
border-bottom: 2px solid var(--tel-border);
|
|
113
|
+
border-radius: 0;
|
|
114
|
+
padding-left: 0;
|
|
115
|
+
padding-right: 34px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
[data-variant="underline"] .ngxsmk-tel-input__control:focus {
|
|
119
|
+
border-bottom-color: var(--tel-ring);
|
|
120
|
+
box-shadow: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ---------- intl-tel-input dropdown (deep selectors) ---------- */
|
|
124
|
+
:host ::ng-deep .iti__flag-container {
|
|
125
|
+
border-top-left-radius: var(--tel-radius);
|
|
126
|
+
border-bottom-left-radius: var(--tel-radius);
|
|
127
|
+
border: 1px solid var(--tel-border);
|
|
128
|
+
border-right: none;
|
|
129
|
+
background: var(--tel-bg);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
:host ::ng-deep .iti__selected-flag {
|
|
133
|
+
height: 100%;
|
|
134
|
+
padding: 0 10px;
|
|
135
|
+
display: inline-flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
:host ::ng-deep .iti__country-list {
|
|
140
|
+
background: var(--tel-dd-bg);
|
|
141
|
+
border: 1px solid var(--tel-dd-border);
|
|
142
|
+
border-radius: var(--tel-dd-radius);
|
|
143
|
+
box-shadow: var(--tel-dd-shadow);
|
|
144
|
+
max-height: min(50vh, 360px);
|
|
145
|
+
overflow: auto;
|
|
146
|
+
padding: 6px 0;
|
|
147
|
+
width: max(280px, 100%);
|
|
148
|
+
z-index: var(--tel-dd-z);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
:host ::ng-deep .iti--container .iti__country-list {
|
|
152
|
+
z-index: var(--tel-dd-z);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
:host ::ng-deep .iti__search-input {
|
|
156
|
+
position: sticky;
|
|
157
|
+
top: 0;
|
|
158
|
+
margin: 0;
|
|
159
|
+
padding: 10px 12px;
|
|
160
|
+
width: 100%;
|
|
161
|
+
border: 0;
|
|
162
|
+
border-bottom: 1px solid var(--tel-dd-border);
|
|
163
|
+
outline: none;
|
|
164
|
+
background: var(--tel-dd-search-bg);
|
|
165
|
+
color: var(--tel-fg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
:host ::ng-deep .iti__country {
|
|
169
|
+
display: grid;
|
|
170
|
+
grid-template-columns: 28px 1fr auto;
|
|
171
|
+
align-items: center;
|
|
172
|
+
column-gap: .5rem;
|
|
173
|
+
padding: 10px 12px;
|
|
174
|
+
cursor: pointer;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
:host ::ng-deep .iti__dial-code {
|
|
178
|
+
color: var(--tel-placeholder);
|
|
179
|
+
font-weight: 600;
|
|
180
|
+
margin-left: 10px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Clear button */
|
|
184
|
+
.ngxsmk-tel__clear {
|
|
185
|
+
position: absolute;
|
|
186
|
+
right: 8px;
|
|
187
|
+
top: 50%;
|
|
188
|
+
transform: translateY(-50%);
|
|
189
|
+
border: 0;
|
|
190
|
+
background: transparent;
|
|
191
|
+
font-size: 18px;
|
|
192
|
+
line-height: 1;
|
|
193
|
+
width: 28px;
|
|
194
|
+
height: 28px;
|
|
195
|
+
border-radius: 50%;
|
|
196
|
+
cursor: pointer;
|
|
197
|
+
color: var(--tel-placeholder);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ngxsmk-tel__clear:hover {
|
|
201
|
+
background: rgba(148, 163, 184, .15);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* Hint & Error */
|
|
205
|
+
.ngxsmk-tel__hint {
|
|
206
|
+
margin-top: 6px;
|
|
207
|
+
font-size: 12px;
|
|
208
|
+
color: var(--tel-placeholder);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ngxsmk-tel__error {
|
|
212
|
+
margin-top: 6px;
|
|
213
|
+
font-size: 12px;
|
|
214
|
+
color: var(--tel-error);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.ngxsmk-tel__wrap.has-error .ngxsmk-tel-input__control {
|
|
218
|
+
border-color: var(--tel-error);
|
|
219
|
+
box-shadow: 0 0 0 3px rgba(239, 68, 68, .15);
|
|
220
|
+
}
|
|
@@ -89,260 +89,45 @@ export interface IntlTelI18n {
|
|
|
89
89
|
}
|
|
90
90
|
</div>
|
|
91
91
|
`,
|
|
92
|
+
styleUrls: ['./ngxsmk-tel-input.component.scss'],
|
|
92
93
|
providers: [
|
|
93
94
|
{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true},
|
|
94
95
|
{provide: NG_VALIDATORS, useExisting: forwardRef(() => NgxsmkTelInputComponent), multi: true}
|
|
95
|
-
]
|
|
96
|
-
styles: [`
|
|
97
|
-
/* ---------- Theme tokens ---------- */
|
|
98
|
-
:host {
|
|
99
|
-
--tel-bg: #fff;
|
|
100
|
-
--tel-fg: #0f172a;
|
|
101
|
-
--tel-border: #c0c0c0;
|
|
102
|
-
--tel-border-hover: #9aa0a6;
|
|
103
|
-
--tel-ring: #2563eb;
|
|
104
|
-
--tel-placeholder: #9ca3af;
|
|
105
|
-
--tel-error: #ef4444;
|
|
106
|
-
--tel-radius: 12px;
|
|
107
|
-
--tel-focus-shadow: 0 0 0 3px rgba(37, 99, 235, .25);
|
|
108
|
-
|
|
109
|
-
--tel-dd-bg: var(--tel-bg);
|
|
110
|
-
--tel-dd-border: var(--tel-border);
|
|
111
|
-
--tel-dd-shadow: 0 24px 60px rgba(0, 0, 0, .18);
|
|
112
|
-
--tel-dd-radius: 12px;
|
|
113
|
-
--tel-dd-item-hover: rgba(37, 99, 235, .08);
|
|
114
|
-
--tel-dd-z: 2000;
|
|
115
|
-
--tel-dd-search-bg: rgba(148, 163, 184, .08);
|
|
116
|
-
|
|
117
|
-
display: block;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
:host-context(.dark) {
|
|
121
|
-
--tel-bg: #0b0f17;
|
|
122
|
-
--tel-fg: #e5e7eb;
|
|
123
|
-
--tel-border: #334155;
|
|
124
|
-
--tel-border-hover: #475569;
|
|
125
|
-
--tel-ring: #60a5fa;
|
|
126
|
-
--tel-placeholder: #94a3b8;
|
|
127
|
-
|
|
128
|
-
--tel-dd-bg: #0f1521;
|
|
129
|
-
--tel-dd-border: #324056;
|
|
130
|
-
--tel-dd-search-bg: rgba(148, 163, 184, .12);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/* ---------- Structure ---------- */
|
|
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
|
-
}
|
|
143
|
-
|
|
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
|
-
}
|
|
158
|
-
|
|
159
|
-
.ngxsmk-tel-input__control {
|
|
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);
|
|
166
|
-
border-radius: var(--tel-radius);
|
|
167
|
-
padding: 10px 40px 10px 12px;
|
|
168
|
-
outline: none;
|
|
169
|
-
transition: border-color .15s, box-shadow .15s, background .15s;
|
|
170
|
-
}
|
|
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
|
-
}
|
|
184
|
-
|
|
185
|
-
[data-size="sm"] .ngxsmk-tel-input__control {
|
|
186
|
-
height: 34px;
|
|
187
|
-
font-size: 13px;
|
|
188
|
-
padding: 6px 36px 6px 10px;
|
|
189
|
-
border-radius: 10px;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
[data-size="lg"] .ngxsmk-tel-input__control {
|
|
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);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
[data-variant="underline"] .ngxsmk-tel-input__control {
|
|
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;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/* ---------- intl-tel-input dropdown (deep selectors) ---------- */
|
|
217
|
-
:host ::ng-deep .iti__flag-container {
|
|
218
|
-
border-top-left-radius: var(--tel-radius);
|
|
219
|
-
border-bottom-left-radius: var(--tel-radius);
|
|
220
|
-
border: 1px solid var(--tel-border);
|
|
221
|
-
border-right: none;
|
|
222
|
-
background: var(--tel-bg);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
:host ::ng-deep .iti__selected-flag {
|
|
226
|
-
height: 100%;
|
|
227
|
-
padding: 0 10px;
|
|
228
|
-
display: inline-flex;
|
|
229
|
-
align-items: center;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
:host ::ng-deep .iti__country-list {
|
|
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);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
:host ::ng-deep .iti__search-input {
|
|
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);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
:host ::ng-deep .iti__country {
|
|
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;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.ngxsmk-tel__clear {
|
|
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);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.ngxsmk-tel__error {
|
|
303
|
-
margin-top: 6px;
|
|
304
|
-
font-size: 12px;
|
|
305
|
-
color: var(--tel-error);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
.ngxsmk-tel__wrap.has-error .ngxsmk-tel-input__control {
|
|
309
|
-
border-color: var(--tel-error);
|
|
310
|
-
box-shadow: 0 0 0 3px rgba(239, 68, 68, .15);
|
|
311
|
-
}
|
|
312
|
-
`]
|
|
96
|
+
]
|
|
313
97
|
})
|
|
314
98
|
export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor, Validator {
|
|
99
|
+
|
|
315
100
|
@ViewChild('telInput', {static: true}) inputRef!: ElementRef<HTMLInputElement>;
|
|
316
101
|
|
|
317
102
|
/* Core config */
|
|
318
103
|
@Input() initialCountry: CountryCode | 'auto' = 'US';
|
|
319
104
|
@Input() preferredCountries: CountryCode[] = ['US', 'GB'];
|
|
320
105
|
@Input() onlyCountries?: CountryCode[];
|
|
321
|
-
@Input() nationalMode = false;
|
|
322
|
-
@Input() separateDialCode = false;
|
|
323
|
-
@Input() allowDropdown = true;
|
|
106
|
+
@Input() nationalMode: boolean = false;
|
|
107
|
+
@Input() separateDialCode: boolean = false;
|
|
108
|
+
@Input() allowDropdown: boolean = true;
|
|
324
109
|
|
|
325
110
|
/* UX */
|
|
326
|
-
@Input() placeholder?: string;
|
|
111
|
+
@Input() placeholder?: string;
|
|
327
112
|
@Input() autocomplete = 'tel';
|
|
328
113
|
@Input() name?: string;
|
|
329
114
|
@Input() inputId?: string;
|
|
330
|
-
@Input() disabled = false;
|
|
115
|
+
@Input() disabled: boolean = false;
|
|
331
116
|
|
|
332
117
|
@Input() label?: string;
|
|
333
118
|
@Input() hint?: string;
|
|
334
119
|
@Input() errorText?: string;
|
|
335
120
|
@Input() size: 'sm' | 'md' | 'lg' = 'md';
|
|
336
121
|
@Input() variant: 'outline' | 'filled' | 'underline' = 'outline';
|
|
337
|
-
@Input() showClear = true;
|
|
338
|
-
@Input() autoFocus = false;
|
|
339
|
-
@Input() selectOnFocus = false;
|
|
340
|
-
@Input() formatOnBlur = true;
|
|
341
|
-
@Input() showErrorWhenTouched = true;
|
|
122
|
+
@Input() showClear: boolean = true;
|
|
123
|
+
@Input() autoFocus: boolean = false;
|
|
124
|
+
@Input() selectOnFocus: boolean = false;
|
|
125
|
+
@Input() formatOnBlur: boolean = true;
|
|
126
|
+
@Input() showErrorWhenTouched: boolean = true;
|
|
342
127
|
|
|
343
128
|
/* Dropdown plumbing */
|
|
344
|
-
@Input() dropdownAttachToBody = true;
|
|
345
|
-
@Input() dropdownZIndex = 2000;
|
|
129
|
+
@Input() dropdownAttachToBody: boolean = true;
|
|
130
|
+
@Input() dropdownZIndex: number = 2000;
|
|
346
131
|
|
|
347
132
|
/* Localization + RTL */
|
|
348
133
|
@Input('i18n') i18n?: IntlTelI18n;
|
|
@@ -357,7 +142,7 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
357
142
|
this.localizedCountries = v;
|
|
358
143
|
}
|
|
359
144
|
|
|
360
|
-
@Input() clearAriaLabel = 'Clear phone number';
|
|
145
|
+
@Input() clearAriaLabel: string = 'Clear phone number';
|
|
361
146
|
@Input() dir: 'ltr' | 'rtl' = 'ltr';
|
|
362
147
|
|
|
363
148
|
/* Placeholders (intl-tel-input) */
|
|
@@ -365,9 +150,11 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
365
150
|
@Input() utilsScript?: string;
|
|
366
151
|
@Input() customPlaceholder?: (example: string, country: any) => string;
|
|
367
152
|
|
|
153
|
+
@Input() formatWhenValid: 'off' | 'blur' | 'typing' = 'blur';
|
|
154
|
+
|
|
368
155
|
/* Digits-only controls */
|
|
369
|
-
@Input() digitsOnly = true;
|
|
370
|
-
@Input() allowLeadingPlus = true;
|
|
156
|
+
@Input() digitsOnly: boolean = true;
|
|
157
|
+
@Input() allowLeadingPlus: boolean = true;
|
|
371
158
|
|
|
372
159
|
/* Outputs */
|
|
373
160
|
@Output() countryChange = new EventEmitter<{ iso2: CountryCode }>();
|
|
@@ -383,9 +170,9 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
383
170
|
private validatorChange?: () => void;
|
|
384
171
|
private lastEmittedValid = false;
|
|
385
172
|
private pendingWrite: string | null = null;
|
|
386
|
-
private touched = false;
|
|
173
|
+
private touched: boolean = false;
|
|
387
174
|
|
|
388
|
-
readonly resolvedId = this.inputId || ('tel-' + Math.random().toString(36).slice(2));
|
|
175
|
+
readonly resolvedId: string = this.inputId || ('tel-' + Math.random().toString(36).slice(2));
|
|
389
176
|
|
|
390
177
|
constructor(
|
|
391
178
|
private readonly zone: NgZone,
|
|
@@ -394,8 +181,12 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
394
181
|
) {
|
|
395
182
|
}
|
|
396
183
|
|
|
397
|
-
|
|
184
|
+
ngAfterViewInit(): void {
|
|
398
185
|
if (!isPlatformBrowser(this.platformId)) return;
|
|
186
|
+
void this.initAndWire();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private async initAndWire(): Promise<void> {
|
|
399
190
|
await this.initIntlTelInput();
|
|
400
191
|
this.bindDomListeners();
|
|
401
192
|
|
|
@@ -503,8 +294,8 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
503
294
|
separateDialCode: this.separateDialCode,
|
|
504
295
|
geoIpLookup: (cb: (iso2: string) => void) => cb('us'),
|
|
505
296
|
|
|
506
|
-
// placeholders
|
|
507
|
-
autoPlaceholder: this.autoPlaceholder,
|
|
297
|
+
// placeholders
|
|
298
|
+
autoPlaceholder: this.autoPlaceholder,
|
|
508
299
|
utilsScript: this.utilsScript,
|
|
509
300
|
customPlaceholder: this.customPlaceholder,
|
|
510
301
|
|
|
@@ -520,7 +311,6 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
520
311
|
this.iti = intlTelInput(this.inputRef.nativeElement, config);
|
|
521
312
|
});
|
|
522
313
|
|
|
523
|
-
// z-index for dropdown
|
|
524
314
|
(this.inputRef.nativeElement as HTMLElement).style.setProperty('--tel-dd-z', String(this.dropdownZIndex));
|
|
525
315
|
}
|
|
526
316
|
|
|
@@ -551,8 +341,7 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
551
341
|
// ----- Input filtering (digits-only) -----
|
|
552
342
|
private sanitizeDigits(value: string): string {
|
|
553
343
|
if (!this.digitsOnly) return value;
|
|
554
|
-
let v = value.replace(/[^\d+]/g, '');
|
|
555
|
-
// allow only ONE leading + (if enabled)
|
|
344
|
+
let v = value.replace(/[^\d+]/g, '');
|
|
556
345
|
if (this.allowLeadingPlus) {
|
|
557
346
|
const hasLeadingPlus = v.startsWith('+');
|
|
558
347
|
v = (hasLeadingPlus ? '+' : '') + v.replace(/\+/g, '');
|
|
@@ -566,12 +355,9 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
566
355
|
const el = this.inputRef.nativeElement;
|
|
567
356
|
|
|
568
357
|
this.zone.runOutsideAngular(() => {
|
|
569
|
-
// prevent invalid chars while typing
|
|
570
358
|
el.addEventListener('beforeinput', (ev: InputEvent) => {
|
|
571
359
|
if (!this.digitsOnly) return;
|
|
572
360
|
const data = (ev as any).data as string | null;
|
|
573
|
-
|
|
574
|
-
// allow deletions, cuts, etc.
|
|
575
361
|
if (!data || ev.inputType !== 'insertText') return;
|
|
576
362
|
|
|
577
363
|
const pos = el.selectionStart ?? 0;
|
|
@@ -581,7 +367,6 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
581
367
|
if (!isDigit && !isPlusAtStart) ev.preventDefault();
|
|
582
368
|
});
|
|
583
369
|
|
|
584
|
-
// sanitize pastes
|
|
585
370
|
el.addEventListener('paste', (e: ClipboardEvent) => {
|
|
586
371
|
if (!this.digitsOnly) return;
|
|
587
372
|
e.preventDefault();
|
|
@@ -593,7 +378,6 @@ export class NgxsmkTelInputComponent implements AfterViewInit, OnChanges, OnDest
|
|
|
593
378
|
queueMicrotask(() => this.handleInput());
|
|
594
379
|
});
|
|
595
380
|
|
|
596
|
-
// catch any remaining non-digit changes (e.g., programmatic)
|
|
597
381
|
el.addEventListener('input', () => {
|
|
598
382
|
if (this.digitsOnly) {
|
|
599
383
|
const val = el.value;
|