├── .all-contributorsrc
├── .editorconfig
├── .github
├── dependabot-github-actions.yaml
└── workflows
│ ├── ci.yml
│ └── test-angular-versions.yaml
├── .gitignore
├── .jsbeautifyrc
├── LICENSE
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── projects
└── ngx-intl-tel-input
│ ├── karma.conf.js
│ ├── ng-package.json
│ ├── package.json
│ ├── src
│ ├── lib
│ │ ├── bootstrap-dropdown.css
│ │ ├── data
│ │ │ └── country-code.ts
│ │ ├── directives
│ │ │ └── native-element-injector.directive.ts
│ │ ├── enums
│ │ │ ├── country-iso.enum.ts
│ │ │ ├── phone-number-format.enum.ts
│ │ │ └── search-country-field.enum.ts
│ │ ├── interfaces
│ │ │ └── change-data.ts
│ │ ├── model
│ │ │ └── country.model.ts
│ │ ├── ngx-intl-tel-input.component.css
│ │ ├── ngx-intl-tel-input.component.html
│ │ ├── ngx-intl-tel-input.component.spec.ts
│ │ ├── ngx-intl-tel-input.component.ts
│ │ ├── ngx-intl-tel-input.module.ts
│ │ └── ngx-intl-tel-input.validator.ts
│ ├── public_api.ts
│ └── test.ts
│ ├── tsconfig.lib.json
│ └── tsconfig.spec.json
├── readme-assets
└── ngx-intl-tel-input.jpg
├── src
├── app
│ ├── app.component.css
│ ├── app.component.html
│ ├── app.component.spec.ts
│ ├── app.component.ts
│ └── app.module.ts
├── assets
│ └── .gitkeep
├── environments
│ ├── environment.prod.ts
│ └── environment.ts
├── favicon.ico
├── index.html
├── karma.conf.js
├── main.ts
├── polyfills.ts
├── styles.css
├── test.ts
├── tsconfig.app.json
└── tsconfig.spec.json
└── tsconfig.json
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "ngx-intl-tel-input",
3 | "projectOwner": "webcat12345",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "imageSize": 100,
10 | "commit": false,
11 | "contributors": [
12 | {
13 | "login": "webcat12345",
14 | "name": "webcat_black",
15 | "avatar_url": "https://avatars3.githubusercontent.com/u/19761422?v=4",
16 | "profile": "https://github.com/webcat12345",
17 | "contributions": [
18 | "design",
19 | "code",
20 | "doc",
21 | "ideas",
22 | "question",
23 | "infra",
24 | "example",
25 | "maintenance",
26 | "review",
27 | "test"
28 | ]
29 | },
30 | {
31 | "login": "cmckni3",
32 | "name": "Chris McKnight",
33 | "avatar_url": "https://avatars0.githubusercontent.com/u/735717?v=4",
34 | "profile": "https://github.com/cmckni3",
35 | "contributions": [
36 | "question",
37 | "code",
38 | "doc",
39 | "ideas",
40 | "infra",
41 | "plugin",
42 | "review",
43 | "tool"
44 | ]
45 | },
46 | {
47 | "login": "pasevin",
48 | "name": "Aleksandr Pasevin",
49 | "avatar_url": "https://avatars2.githubusercontent.com/u/1058469?v=4",
50 | "profile": "http://pasevin.com",
51 | "contributions": [
52 | "code",
53 | "doc",
54 | "bug",
55 | "platform",
56 | "plugin",
57 | "question",
58 | "infra",
59 | "example",
60 | "maintenance",
61 | "review",
62 | "test"
63 | ]
64 | },
65 | {
66 | "login": "Dviejopomata",
67 | "name": "Dviejo",
68 | "avatar_url": "https://avatars0.githubusercontent.com/u/6862893?v=4",
69 | "profile": "https://github.com/Dviejopomata",
70 | "contributions": [
71 | "code",
72 | "bug",
73 | "example",
74 | "test",
75 | "question",
76 | "maintenance",
77 | "review"
78 | ]
79 | },
80 | {
81 | "login": "kinoroy",
82 | "name": "Kino Roy",
83 | "avatar_url": "https://avatars3.githubusercontent.com/u/22554212?v=4",
84 | "profile": "http://kino.codes",
85 | "contributions": [
86 | "code"
87 | ]
88 | },
89 | {
90 | "login": "NathanWalker",
91 | "name": "Nathan Walker",
92 | "avatar_url": "https://avatars2.githubusercontent.com/u/457187?v=4",
93 | "profile": "https://twitter.com/wwwalkerrun",
94 | "contributions": [
95 | "maintenance",
96 | "code"
97 | ]
98 | },
99 | {
100 | "login": "jiarongxu",
101 | "name": "Jiarong Xu",
102 | "avatar_url": "https://avatars0.githubusercontent.com/u/502605?v=4",
103 | "profile": "https://github.com/jiarongxu",
104 | "contributions": [
105 | "code"
106 | ]
107 | },
108 | {
109 | "login": "crutchcorn",
110 | "name": "Corbin Crutchley",
111 | "avatar_url": "https://avatars.githubusercontent.com/u/9100169?v=4",
112 | "profile": "https://crutchcorn.dev",
113 | "contributions": [
114 | "code"
115 | ]
116 | },
117 | {
118 | "login": "NayeBeckham",
119 | "name": "Nayeli Beckham",
120 | "avatar_url": "https://avatars.githubusercontent.com/u/78670199?v=4",
121 | "profile": "https://www.linkedin.com/in/nayeli-beckham-martínez/",
122 | "contributions": [
123 | "code",
124 | "platform",
125 | "question",
126 | "maintenance"
127 | ]
128 | },
129 | {
130 | "login": "rushvora",
131 | "name": "Rushabh Vora",
132 | "avatar_url": "https://avatars.githubusercontent.com/u/602333?v=4",
133 | "profile": "http://rushvora.com",
134 | "contributions": [
135 | "code"
136 | ]
137 | },
138 | {
139 | "login": "KarimTayie",
140 | "name": "Karim Tayie",
141 | "avatar_url": "https://avatars.githubusercontent.com/u/29521303?v=4",
142 | "profile": "https://github.com/KarimTayie",
143 | "contributions": [
144 | "code"
145 | ]
146 | },
147 | {
148 | "login": "imadilkhalil",
149 | "name": "Adil Khalil",
150 | "avatar_url": "https://avatars.githubusercontent.com/u/19613930?v=4",
151 | "profile": "https://github.com/imadilkhalil",
152 | "contributions": [
153 | "code"
154 | ]
155 | },
156 | {
157 | "login": "ambersz",
158 | "name": "Alice Zhao",
159 | "avatar_url": "https://avatars.githubusercontent.com/u/647791?v=4",
160 | "profile": "https://github.com/ambersz",
161 | "contributions": [
162 | "code"
163 | ]
164 | },
165 | {
166 | "login": "felipecespedes",
167 | "name": "Felipe Céspedes",
168 | "avatar_url": "https://avatars.githubusercontent.com/u/11846311?v=4",
169 | "profile": "https://www.felipecespedes.co",
170 | "contributions": [
171 | "code"
172 | ]
173 | },
174 | {
175 | "login": "bb-sonam",
176 | "name": "bb-sonam",
177 | "avatar_url": "https://avatars.githubusercontent.com/u/60337996?v=4",
178 | "profile": "https://github.com/bb-sonam",
179 | "contributions": [
180 | "code"
181 | ]
182 | },
183 | {
184 | "login": "alQlagin",
185 | "name": "Alex Kulagin",
186 | "avatar_url": "https://avatars.githubusercontent.com/u/3256488?v=4",
187 | "profile": "https://github.com/alQlagin",
188 | "contributions": [
189 | "code"
190 | ]
191 | },
192 | {
193 | "login": "subodhyadav712",
194 | "name": "Subodh Kumar Yadav",
195 | "avatar_url": "https://avatars.githubusercontent.com/u/36027830?v=4",
196 | "profile": "https://github.com/subodhyadav712",
197 | "contributions": [
198 | "code"
199 | ]
200 | },
201 | {
202 | "login": "harshalganbote",
203 | "name": "harshalganbote",
204 | "avatar_url": "https://avatars.githubusercontent.com/u/60728890?v=4",
205 | "profile": "https://github.com/harshalganbote",
206 | "contributions": [
207 | "code"
208 | ]
209 | },
210 | {
211 | "login": "christianopaets",
212 | "name": "christianopaets",
213 | "avatar_url": "https://avatars.githubusercontent.com/u/43338174?v=4",
214 | "profile": "https://github.com/christianopaets",
215 | "contributions": [
216 | "code"
217 | ]
218 | },
219 | {
220 | "login": "velechva",
221 | "name": "Victor Velechovsky",
222 | "avatar_url": "https://avatars.githubusercontent.com/u/13340931?v=4",
223 | "profile": "https://github.com/velechva",
224 | "contributions": [
225 | "code"
226 | ]
227 | },
228 | {
229 | "login": "leo6104",
230 | "name": "Heo",
231 | "avatar_url": "https://avatars.githubusercontent.com/u/7777929?v=4",
232 | "profile": "https://www.mapianist.com",
233 | "contributions": [
234 | "code"
235 | ]
236 | },
237 | {
238 | "login": "riderx",
239 | "name": "Martin DONADIEU",
240 | "avatar_url": "https://avatars.githubusercontent.com/u/4084527?v=4",
241 | "profile": "https://martin.solos.ventures/",
242 | "contributions": [
243 | "code",
244 | "userTesting"
245 | ]
246 | },
247 | {
248 | "login": "nicolasl-alight",
249 | "name": "Nico",
250 | "avatar_url": "https://avatars.githubusercontent.com/u/133889962?v=4",
251 | "profile": "https://github.com/nicolasl-alight",
252 | "contributions": [
253 | "code",
254 | "doc",
255 | "platform",
256 | "userTesting"
257 | ]
258 | },
259 | {
260 | "login": "zaizac",
261 | "name": "zaizac",
262 | "avatar_url": "https://avatars.githubusercontent.com/u/9007583?v=4",
263 | "profile": "https://github.com/zaizac",
264 | "contributions": [
265 | "code",
266 | "review"
267 | ]
268 | },
269 | {
270 | "login": "nicolasleb",
271 | "name": "nicolasleb",
272 | "avatar_url": "https://avatars.githubusercontent.com/u/4135062?v=4",
273 | "profile": "https://github.com/nicolasleb",
274 | "contributions": [
275 | "code",
276 | "maintenance",
277 | "platform",
278 | "review",
279 | "userTesting"
280 | ]
281 | },
282 | {
283 | "login": "jljabben",
284 | "name": "jlj",
285 | "avatar_url": "https://avatars.githubusercontent.com/u/5000255?v=4",
286 | "profile": "https://github.com/jljabben",
287 | "contributions": [
288 | "code",
289 | "userTesting",
290 | "bug"
291 | ]
292 | },
293 | {
294 | "login": "ahmedasif1",
295 | "name": "Ahmed Asif",
296 | "avatar_url": "https://avatars.githubusercontent.com/u/5271212?v=4",
297 | "profile": "https://github.com/ahmedasif1",
298 | "contributions": [
299 | "bug",
300 | "code"
301 | ]
302 | },
303 | {
304 | "login": "snmhinge",
305 | "name": "snmhinge",
306 | "avatar_url": "https://avatars.githubusercontent.com/u/10630821?v=4",
307 | "profile": "https://github.com/snmhinge",
308 | "contributions": [
309 | "code"
310 | ]
311 | },
312 | {
313 | "login": "juju-sunrise",
314 | "name": "juju-sunrise",
315 | "avatar_url": "https://avatars.githubusercontent.com/u/139119987?v=4",
316 | "profile": "https://github.com/juju-sunrise",
317 | "contributions": [
318 | "code",
319 | "maintenance"
320 | ]
321 | },
322 | {
323 | "login": "danielstvn",
324 | "name": "danielstvn",
325 | "avatar_url": "https://avatars.githubusercontent.com/u/83106033?v=4",
326 | "profile": "https://github.com/danielstvn",
327 | "contributions": [
328 | "code",
329 | "maintenance"
330 | ]
331 | },
332 | {
333 | "login": "kapcake",
334 | "name": "kapcake",
335 | "avatar_url": "https://avatars.githubusercontent.com/u/9072781?v=4",
336 | "profile": "https://github.com/kapcake",
337 | "contributions": [
338 | "code",
339 | "maintenance"
340 | ]
341 | }
342 | ],
343 | "contributorsPerLine": 7,
344 | "skipCi": true,
345 | "commitConvention": "angular",
346 | "commitType": "docs"
347 | }
348 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see https://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.ts]
13 | quote_type = single
14 |
15 | [*.{md,rst}]
16 | max_line_length = off
17 | insert_final_newline = false
18 | trim_trailing_whitespace = false
19 |
--------------------------------------------------------------------------------
/.github/dependabot-github-actions.yaml:
--------------------------------------------------------------------------------
1 | # Set update schedule for GitHub Actions
2 |
3 | version: 2
4 | updates:
5 |
6 | - package-ecosystem: "github-actions"
7 | directory: "/"
8 | schedule:
9 | # Check for updates to GitHub Actions every week
10 | interval: "weekly"
11 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | tags-ignore:
6 | - '**'
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout 🛎️
13 | uses: actions/checkout@v4
14 |
15 | - name: Install and Build 🔧
16 | run: |
17 | npm ci
18 | npm run build_lib
19 | npm run build
20 |
21 |
--------------------------------------------------------------------------------
/.github/workflows/test-angular-versions.yaml:
--------------------------------------------------------------------------------
1 | name: Test Angular Versions
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | test-angular-versions:
8 | runs-on: ubuntu-latest
9 |
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | angular_version: ["16", "17", "18", "19"]
14 |
15 | steps:
16 | - name: Checkout 🔔
17 | uses: actions/checkout@v4
18 |
19 | - name: Set up Node.js
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: 18
23 |
24 | - name: Install dependencies
25 | run: npm ci
26 |
27 | - name: Determine TypeScript version for Angular ${{ matrix.angular_version }} 🔄
28 | run: |
29 | case ${{ matrix.angular_version }} in
30 | "15") TS_VERSION="~4.8.0" ;;
31 | "16") TS_VERSION="~4.9.0" ;;
32 | "17") TS_VERSION="~5.2.0" ;;
33 | "18") TS_VERSION="~5.5.0" ;;
34 | "19") TS_VERSION="~5.5.0" ;;
35 | *) echo "Unsupported Angular version"; exit 1 ;;
36 | esac
37 | echo "Using TypeScript version $TS_VERSION"
38 | npm install typescript@$TS_VERSION --save-dev --force
39 |
40 | - name: Install Angular version ${{ matrix.angular_version }}
41 | run: |
42 | npm run ng version
43 |
44 | # Angular runtime dependencies
45 | npm install @angular/animations@${{ matrix.angular_version }} --save --force
46 | npm install @angular/common@${{ matrix.angular_version }} --save --force
47 | npm install @angular/compiler@${{ matrix.angular_version }} --save --force
48 | npm install @angular/core@${{ matrix.angular_version }} --save --force
49 | npm install @angular/forms@${{ matrix.angular_version }} --save --force
50 | npm install @angular/platform-browser@${{ matrix.angular_version }} --save --force
51 | npm install @angular/platform-browser-dynamic@${{ matrix.angular_version }} --save --force
52 | npm install @angular/router@${{ matrix.angular_version }} --save --force
53 |
54 | # Angular dev dependencies
55 | npm install @angular-devkit/build-angular@${{ matrix.angular_version }} --save-dev --force
56 | npm install @angular/cli@${{ matrix.angular_version }} --save-dev --force
57 | npm install @angular/compiler-cli@${{ matrix.angular_version }} --save-dev --force
58 | npm install @angular/language-service@${{ matrix.angular_version }} --save-dev --force
59 |
60 | npm install --force
61 |
62 | npm run ng update @angular/cli@${{ matrix.angular_version }} -- --force --allow-dirty
63 | npm run ng update @angular/core@${{ matrix.angular_version }} -- --force --allow-dirty
64 | npm run ng update -- --force --allow-dirty
65 |
66 | - name: Run Angular build 🔧
67 | run: npm run build
68 |
69 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | npm-debug.log
12 | yarn-error.log
13 |
14 | # IDEs and editors
15 | .idea/
16 | .project
17 | .classpath
18 | .c9/
19 | *.launch
20 | .settings/
21 | *.sublime-workspace
22 |
23 | # Visual Studio Code
24 | .vscode/*
25 | !.vscode/settings.json
26 | !.vscode/tasks.json
27 | !.vscode/launch.json
28 | !.vscode/extensions.json
29 | .history/*
30 |
31 | # Miscellaneous
32 | /.angular/cache
33 | .sass-cache/
34 | /connect.lock
35 | /coverage
36 | /libpeerconnection.log
37 | testem.log
38 | /typings
39 |
40 | # System files
41 | .DS_Store
42 | Thumbs.db
43 |
--------------------------------------------------------------------------------
/.jsbeautifyrc:
--------------------------------------------------------------------------------
1 | {
2 | "indent_inner_html": true,
3 | "indent_with_tabs": true,
4 | "tab_size": 2,
5 | "wrap_attributes": "force"
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 webcat12345
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # International Telephone Input for Angular (NgxIntlTelInput)
2 |
3 | [](https://github.com/webcat12345/ngx-intl-tel-input/actions/workflows/ci.yml)
4 | [](https://webcat12345.github.io/ngx-intl-tel-input/demo/)
5 | [](https://github.com/webcat12345/ngx-intl-tel-input/blob/master/LICENSE)
6 | [](https://badge.fury.io/js/ngx-intl-tel-input)
7 | [](https://www.npmjs.com/package/ngx-intl-tel-input)
8 |
9 | [](#contributors)
10 |
11 | An Angular package for entering and validating international telephone numbers. It adds a flag dropdown to any input, detects the user's country, displays a relevant placeholder and provides formatting/validation methods.
12 |
13 | 
14 |
15 | **Compatibility:**
16 |
17 | Validation with [google-libphonenumber](https://github.com/ruimarinho/google-libphonenumber)
18 |
19 | | ngx-intl-tel-input | Angular | ngx-bootstrap |
20 | | ------------------ |-----------------| ------------- |
21 | | 17.x.x | >= 17.x.x | >= 12.0.0 |
22 | | 16.x.x | >= 16.x.x | >= 11.0.0 |
23 | | 15.x.x | >= 15.x.x | >= 10.0.0 |
24 | | 14.x.x | 12.x.x - 14.x.x | >= 6.0.0 |
25 | | 13.x.x | 12.x.x - 13.x.x | >= 6.0.0 |
26 | | 3.2.1 | 12.x.x - 13.x.x | >= 6.0.0 |
27 | | 3.1.3 | 8.x.x - 11.x.x | 6.0.x |
28 | | 2.x.x | 8.x.x - 9.1.x | 5.6.x |
29 |
30 | ## Installation
31 |
32 | ### Install Dependencies
33 |
34 | `$ npm install intl-tel-input@19 --save`
35 |
36 | `$ npm install google-libphonenumber --save`
37 |
38 | `$ ng add ngx-bootstrap`
39 |
40 | If you do not wish to use Bootstrap's global CSS, we now package the project with only the relevant
41 | bootstrap styling needed for the dropdown. As such, you can remove the bootstrap styling from `angular.json`.
42 |
43 | Further, Angular CLI should tree-shake the rest of Ngx-Boostrap away if you don't utilize other dependencies from
44 | the bootstrap package. This should keep this dependency a lean feature-add
45 |
46 | ### Add Dependency Style
47 |
48 | Add _'intl-tel-input'_ style file:
49 |
50 | `./node_modules/intl-tel-input/build/css/intlTelInput.css`
51 |
52 | to **angular.json** styles array:
53 |
54 | ```json
55 |
56 | "styles": [
57 | "./node_modules/intl-tel-input/build/css/intlTelInput.css",
58 | "src/styles.css"
59 | ],
60 |
61 | ```
62 |
63 | ### Install This Library
64 |
65 | `$ npm install ngx-intl-tel-input --save`
66 |
67 | ## Usage
68 |
69 | ### Import
70 |
71 | Add `NgxIntlTelInputModule` to your module file:
72 |
73 | ```javascript
74 | imports: [NgxIntlTelInputModule];
75 | ```
76 |
77 | ## Example
78 |
79 | Refer to main app in this repository for working example.
80 |
81 | Or this:
82 |
83 | [Stackblitz Demo (Angular 8)](https://stackblitz.com/edit/ngx-intl-tel-input-demo-ng-8)
84 |
85 | [Stackblitz Demo (Angular 9)](https://stackblitz.com/edit/ngx-intl-tel-input-demo-ng-9)
86 |
87 | [Stackblitz Demo (Angular 10)](https://stackblitz.com/edit/ngx-intl-tel-input-demo-ng-10)
88 |
89 | [Stackblitz Demo (Angular 11)](https://stackblitz.com/edit/ngx-intl-tel-input-demo-ng-11)
90 |
91 | [Stackblitz Demo (Angular 12)](https://stackblitz.com/edit/ngx-intl-tel-input-demo-ng-12)
92 |
93 | ```html
94 |
111 | ```
112 |
113 | ## Options
114 |
115 | | Options | Type | Default | Description |
116 | | ------------------------ | ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------- |
117 | | cssClass | `string` | `control-form` | Bootstrap input css class or your own custom one. |
118 | | preferredCountries | `[]` | `[]` | List of countries, which will appear at the top. |
119 | | onlyCountries | `[]` | `[]` | List of manually selected countries, which will appear in the dropdown. |
120 | | enableAutoCountrySelect | `boolean` | `true` | Toggle automatic country (flag) selection based on user input. |
121 | | enablePlaceholder | `boolean` | `true` | Input placeholder text, which adapts to the country selected. |
122 | | customPlaceholder | `string` | `None` | Custom string to be inserted as a placeholder. |
123 | | numberFormat | `` | `PhoneNumberFormat.International` | Custom string to be inserted as a placeholder. |
124 | | searchCountryFlag | `boolean` | `false` | Enables input search box for countries in the flag dropdown. |
125 | | searchCountryField | `[]` | `[SearchCountryField.All]` | Customize which fields to search in, if `searchCountryFlag` is enabled. Use `SearchCountryField` helper enum. |
126 | | searchCountryPlaceholder | `string` | `'Search Country'` | Placeholder value for `searchCountryField` |
127 | | maxLength | `number` | `None` | Add character limit. |
128 | | selectFirstCountry | `boolean` | `true` | Selects first country from `preferredCountries` if is set. If not then uses main list. |
129 | | phoneValidation | `boolean` | `true` | Disable phone validation. |
130 | | inputId | `string` | `phone` | Unique ID for `` element. |
131 | | selectedCountryISO | `` | `None` | Set specific country on load. |
132 | | separateDialCode | `boolean` | `false` | Visually separate dialcode into the drop down element. |
133 | | countryChange | `` | `None` | Emits country value when the user selects a country from the dropdown. |
134 |
135 | ## Supported Formats
136 |
137 | Following formats are supported
138 |
139 | - NATIONAL // Produces "044 668 18 00"
140 | - INTERNATIONAL // Produces "+41 44 668 18 00"
141 | - E164 // Produces "+41446681800"
142 |
143 | ## Library Contributions
144 |
145 | - Fork repo.
146 | - Update `./projects/ngx-intl-tel-input`
147 | - Build / test library.
148 | - Update `./src/app` with new functionality.
149 | - Update README.md
150 | - Pull request.
151 |
152 | ### Helpful commands
153 |
154 | - Build lib: `$ npm run build_lib`
155 | - Copy license and readme files: `$ npm run copy-files`
156 | - Create package: `$ npm run npm_pack`
157 | - Build lib and create package: `$ npm run package`
158 |
159 | ### Use locally
160 |
161 | After building and creating package, you can use it locally too.
162 |
163 | In your project run:
164 |
165 | `$ npm install --save {{path to your local '*.tgz' package file}}`
166 |
167 | ## Contributors
168 |
169 | Thanks goes to these wonderful people ([emoji key](https://github.com/all-contributors/all-contributors#emoji-key)):
170 |
171 |
172 |
173 |
174 |
219 |
220 |
221 |
222 |
223 |
224 |
225 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
226 |
--------------------------------------------------------------------------------
/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "ngx-intl-tel-input-app": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "dist/ngx-intl-tel-input-app",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "src/tsconfig.app.json",
21 | "assets": ["src/favicon.ico", "src/assets"],
22 | "styles": [
23 | "./node_modules/intl-tel-input/build/css/intlTelInput.css",
24 | "src/styles.css"
25 | ],
26 | "scripts": [],
27 | "allowedCommonJsDependencies": ["google-libphonenumber"],
28 | "vendorChunk": true,
29 | "extractLicenses": false,
30 | "buildOptimizer": false,
31 | "sourceMap": true,
32 | "optimization": false,
33 | "namedChunks": true
34 | },
35 | "configurations": {
36 | "production": {
37 | "fileReplacements": [
38 | {
39 | "replace": "src/environments/environment.ts",
40 | "with": "src/environments/environment.prod.ts"
41 | }
42 | ],
43 | "optimization": true,
44 | "outputHashing": "all",
45 | "sourceMap": false,
46 | "namedChunks": false,
47 | "extractLicenses": true,
48 | "vendorChunk": false,
49 | "buildOptimizer": true,
50 | "budgets": [
51 | {
52 | "type": "initial",
53 | "maximumWarning": "2mb",
54 | "maximumError": "5mb"
55 | },
56 | {
57 | "type": "anyComponentStyle",
58 | "maximumWarning": "6kb"
59 | }
60 | ]
61 | }
62 | },
63 | "defaultConfiguration": ""
64 | },
65 | "serve": {
66 | "builder": "@angular-devkit/build-angular:dev-server",
67 | "options": {
68 | "buildTarget": "ngx-intl-tel-input-app:build"
69 | },
70 | "configurations": {
71 | "production": {
72 | "buildTarget": "ngx-intl-tel-input-app:build:production"
73 | }
74 | }
75 | },
76 | "extract-i18n": {
77 | "builder": "@angular-devkit/build-angular:extract-i18n",
78 | "options": {
79 | "buildTarget": "ngx-intl-tel-input-app:build"
80 | }
81 | },
82 | "test": {
83 | "builder": "@angular-devkit/build-angular:karma",
84 | "options": {
85 | "main": "src/test.ts",
86 | "polyfills": "src/polyfills.ts",
87 | "tsConfig": "src/tsconfig.spec.json",
88 | "karmaConfig": "src/karma.conf.js",
89 | "styles": [
90 | "./node_modules/intl-tel-input/build/css/intlTelInput.css",
91 | "src/styles.css"
92 | ],
93 | "scripts": [],
94 | "assets": ["src/favicon.ico", "src/assets"]
95 | }
96 | }
97 | }
98 | },
99 | "ngx-intl-tel-input": {
100 | "root": "projects/ngx-intl-tel-input",
101 | "sourceRoot": "projects/ngx-intl-tel-input/src",
102 | "projectType": "library",
103 | "prefix": "ngx",
104 | "architect": {
105 | "build": {
106 | "builder": "@angular-devkit/build-angular:ng-packagr",
107 | "options": {
108 | "tsConfig": "projects/ngx-intl-tel-input/tsconfig.lib.json",
109 | "project": "projects/ngx-intl-tel-input/ng-package.json"
110 | }
111 | },
112 | "test": {
113 | "builder": "@angular-devkit/build-angular:karma",
114 | "options": {
115 | "main": "projects/ngx-intl-tel-input/src/test.ts",
116 | "tsConfig": "projects/ngx-intl-tel-input/tsconfig.spec.json",
117 | "karmaConfig": "projects/ngx-intl-tel-input/karma.conf.js"
118 | }
119 | }
120 | }
121 | }
122 | },
123 | "cli": {
124 | "analytics": false
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-intl-tel-input-app",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e",
11 | "build_stats": "ng build --stats-json",
12 | "analyze": "webpack-bundle-analyzer dist/ngx-intl-tel-input-app/stats.json",
13 | "build_lib": "ng build ngx-intl-tel-input",
14 | "copy-license": "cp ./LICENSE ./dist/ngx-intl-tel-input",
15 | "copy-readme": "cp ./README.md ./dist/ngx-intl-tel-input",
16 | "copy-files": "npm run copy-license && npm run copy-readme",
17 | "npm_pack": "npm run copy-files && cd dist/ngx-intl-tel-input && npm pack",
18 | "package": "rimraf ./dist && npm run build_lib && npm run npm_pack"
19 | },
20 | "private": true,
21 | "dependencies": {
22 | "@angular/animations": "^17.0.0",
23 | "@angular/common": "^17.0.0",
24 | "@angular/compiler": "^17.0.0",
25 | "@angular/core": "^17.0.0",
26 | "@angular/forms": "^17.0.0",
27 | "@angular/platform-browser": "^17.0.0",
28 | "@angular/platform-browser-dynamic": "^17.0.0",
29 | "@angular/router": "^17.0.0",
30 | "bootstrap": "^5.3.0",
31 | "core-js": "^3.35.0",
32 | "google-libphonenumber": "^3.2.34",
33 | "intl-tel-input": "^19.2.3",
34 | "jquery": "^3.7.1",
35 | "ngx-bootstrap": "^12.0.0",
36 | "popper.js": "^1.16.1",
37 | "rxjs": "^7.8.1",
38 | "tslib": "^2.6.2",
39 | "zone.js": "~0.14.3"
40 | },
41 | "devDependencies": {
42 | "@angular-devkit/build-angular": "^17.0.0",
43 | "@angular/cli": "^17.0.0",
44 | "@angular/compiler-cli": "^17.0.0",
45 | "@angular/language-service": "^17.0.0",
46 | "@types/google-libphonenumber": "^7.4.30",
47 | "@types/jasmine": "^5.1.4",
48 | "@types/jasminewd2": "^2.0.13",
49 | "@types/node": "^20.11.5",
50 | "ajv": "^8.12.0",
51 | "eslint": "^8.56.0",
52 | "jasmine-core": "^5.1.1",
53 | "jasmine-spec-reporter": "^7.0.0",
54 | "karma": "^6.4.2",
55 | "karma-chrome-launcher": "^3.2.0",
56 | "karma-coverage-istanbul-reporter": "^3.0.3",
57 | "karma-jasmine": "^5.1.0",
58 | "karma-jasmine-html-reporter": "^2.1.0",
59 | "ng-packagr": "^17.0.0",
60 | "prettier": "^3.2.4",
61 | "rollup": "^4.9.5",
62 | "ts-node": "^10.9.2",
63 | "typescript": "~5.4.0",
64 | "webpack-bundle-analyzer": "^4.10.1"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma'),
14 | ],
15 | client: {
16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../../coverage'),
20 | reports: ['html', 'lcovonly'],
21 | fixWebpackSourcePaths: true,
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | customLaunchers: {
29 | ChromeHeadless: {
30 | base: 'Chrome',
31 | flags: [
32 | '--headless',
33 | '--disable-gpu',
34 | '--no-sandbox',
35 | '--remote-debugging-port=9222',
36 | '--disable-web-security',
37 | ],
38 | },
39 | },
40 | browsers: ['Chrome'],
41 | singleRun: false,
42 | });
43 | };
44 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/ng-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3 | "dest": "../../dist/ngx-intl-tel-input",
4 | "lib": {
5 | "entryFile": "src/public_api.ts"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ngx-intl-tel-input",
3 | "version": "17.0.0",
4 | "peerDependencies": {
5 | "@angular/common": ">= 17.0.0",
6 | "@angular/core": ">= 17.0.0",
7 | "@angular/forms": ">= 17.0.0",
8 | "google-libphonenumber": "^3.0.0",
9 | "intl-tel-input": "^19.0.0",
10 | "ngx-bootstrap": ">= 12.0.0"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/webcat12345/ngx-intl-tel-input.git"
15 | },
16 | "author": {
17 | "name": "webcat12345",
18 | "email": "webcat91@gmail.com"
19 | },
20 | "keywords": [
21 | "angular",
22 | "angular 2",
23 | "ng8",
24 | "ng9",
25 | "ng10",
26 | "ng11",
27 | "ng12",
28 | "ng13",
29 | "ng14",
30 | "ng15",
31 | "ng16",
32 | "ng17",
33 | "angular 8",
34 | "angular 9",
35 | "angular 10",
36 | "angular 11",
37 | "angular 12",
38 | "angular 13",
39 | "angular 14",
40 | "angular 15",
41 | "angular 16",
42 | "angular 17",
43 | "intl-tel-input",
44 | "phone number",
45 | "validation"
46 | ],
47 | "license": "MIT",
48 | "bugs": {
49 | "url": "https://github.com/webcat12345/ngx-intl-tel-input/issues"
50 | },
51 | "devDependencies": {
52 | "@types/google-libphonenumber": "^7.4.24"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/bootstrap-dropdown.css:
--------------------------------------------------------------------------------
1 | .dropup,
2 | .dropright,
3 | .dropdown,
4 | .dropleft {
5 | position: relative;
6 | }
7 |
8 | .dropdown-toggle {
9 | white-space: nowrap;
10 | }
11 | .dropdown-toggle::after {
12 | display: inline-block;
13 | margin-left: 0.255em;
14 | vertical-align: 0.255em;
15 | content: "";
16 | border-top: 0.3em solid;
17 | border-right: 0.3em solid transparent;
18 | border-bottom: 0;
19 | border-left: 0.3em solid transparent;
20 | }
21 | .dropdown-toggle:empty::after {
22 | margin-left: 0;
23 | }
24 |
25 | .dropdown-menu {
26 | position: absolute;
27 | top: 100%;
28 | left: 0;
29 | z-index: 1000;
30 | display: none;
31 | float: left;
32 | min-width: 10rem;
33 | padding: 0.5rem 0;
34 | margin: 0.125rem 0 0;
35 | font-size: 1rem;
36 | color: #212529;
37 | text-align: left;
38 | list-style: none;
39 | background-color: #fff;
40 | background-clip: padding-box;
41 | border: 1px solid rgba(0, 0, 0, 0.15);
42 | border-radius: 0.25rem;
43 | }
44 |
45 | .dropdown-menu-left {
46 | right: auto;
47 | left: 0;
48 | }
49 |
50 | .dropdown-menu-right {
51 | right: 0;
52 | left: auto;
53 | }
54 |
55 | @media (min-width: 576px) {
56 | .dropdown-menu-sm-left {
57 | right: auto;
58 | left: 0;
59 | }
60 |
61 | .dropdown-menu-sm-right {
62 | right: 0;
63 | left: auto;
64 | }
65 | }
66 | @media (min-width: 768px) {
67 | .dropdown-menu-md-left {
68 | right: auto;
69 | left: 0;
70 | }
71 |
72 | .dropdown-menu-md-right {
73 | right: 0;
74 | left: auto;
75 | }
76 | }
77 | @media (min-width: 992px) {
78 | .dropdown-menu-lg-left {
79 | right: auto;
80 | left: 0;
81 | }
82 |
83 | .dropdown-menu-lg-right {
84 | right: 0;
85 | left: auto;
86 | }
87 | }
88 | @media (min-width: 1200px) {
89 | .dropdown-menu-xl-left {
90 | right: auto;
91 | left: 0;
92 | }
93 |
94 | .dropdown-menu-xl-right {
95 | right: 0;
96 | left: auto;
97 | }
98 | }
99 | .dropup .dropdown-menu {
100 | top: auto;
101 | bottom: 100%;
102 | margin-top: 0;
103 | margin-bottom: 0.125rem;
104 | }
105 | .dropup .dropdown-toggle::after {
106 | display: inline-block;
107 | margin-left: 0.255em;
108 | vertical-align: 0.255em;
109 | content: "";
110 | border-top: 0;
111 | border-right: 0.3em solid transparent;
112 | border-bottom: 0.3em solid;
113 | border-left: 0.3em solid transparent;
114 | }
115 | .dropup .dropdown-toggle:empty::after {
116 | margin-left: 0;
117 | }
118 |
119 | .dropright .dropdown-menu {
120 | top: 0;
121 | right: auto;
122 | left: 100%;
123 | margin-top: 0;
124 | margin-left: 0.125rem;
125 | }
126 | .dropright .dropdown-toggle::after {
127 | display: inline-block;
128 | margin-left: 0.255em;
129 | vertical-align: 0.255em;
130 | content: "";
131 | border-top: 0.3em solid transparent;
132 | border-right: 0;
133 | border-bottom: 0.3em solid transparent;
134 | border-left: 0.3em solid;
135 | }
136 | .dropright .dropdown-toggle:empty::after {
137 | margin-left: 0;
138 | }
139 | .dropright .dropdown-toggle::after {
140 | vertical-align: 0;
141 | }
142 |
143 | .dropleft .dropdown-menu {
144 | top: 0;
145 | right: 100%;
146 | left: auto;
147 | margin-top: 0;
148 | margin-right: 0.125rem;
149 | }
150 | .dropleft .dropdown-toggle::after {
151 | display: inline-block;
152 | margin-left: 0.255em;
153 | vertical-align: 0.255em;
154 | content: "";
155 | }
156 | .dropleft .dropdown-toggle::after {
157 | display: none;
158 | }
159 | .dropleft .dropdown-toggle::before {
160 | display: inline-block;
161 | margin-right: 0.255em;
162 | vertical-align: 0.255em;
163 | content: "";
164 | border-top: 0.3em solid transparent;
165 | border-right: 0.3em solid;
166 | border-bottom: 0.3em solid transparent;
167 | }
168 | .dropleft .dropdown-toggle:empty::after {
169 | margin-left: 0;
170 | }
171 | .dropleft .dropdown-toggle::before {
172 | vertical-align: 0;
173 | }
174 |
175 | .dropdown-menu[x-placement^=top], .dropdown-menu[x-placement^=right], .dropdown-menu[x-placement^=bottom], .dropdown-menu[x-placement^=left] {
176 | right: auto;
177 | bottom: auto;
178 | }
179 |
180 | .dropdown-divider {
181 | height: 0;
182 | margin: 0.5rem 0;
183 | overflow: hidden;
184 | border-top: 1px solid #e9ecef;
185 | }
186 |
187 | .dropdown-item {
188 | display: block;
189 | width: 100%;
190 | padding: 0.25rem 1.5rem;
191 | clear: both;
192 | font-weight: 400;
193 | color: #212529;
194 | text-align: inherit;
195 | white-space: nowrap;
196 | background-color: transparent;
197 | border: 0;
198 | }
199 | .dropdown-item:hover, .dropdown-item:focus {
200 | color: #16181b;
201 | text-decoration: none;
202 | background-color: #f8f9fa;
203 | }
204 | .dropdown-item.active, .dropdown-item:active {
205 | color: #fff;
206 | text-decoration: none;
207 | background-color: #007bff;
208 | }
209 | .dropdown-item.disabled, .dropdown-item:disabled {
210 | color: #6c757d;
211 | pointer-events: none;
212 | background-color: transparent;
213 | }
214 |
215 | .dropdown-menu.show {
216 | display: block;
217 | }
218 |
219 | .dropdown-header {
220 | display: block;
221 | padding: 0.5rem 1.5rem;
222 | margin-bottom: 0;
223 | font-size: 0.875rem;
224 | color: #6c757d;
225 | white-space: nowrap;
226 | }
227 |
228 | .dropdown-item-text {
229 | display: block;
230 | padding: 0.25rem 1.5rem;
231 | color: #212529;
232 | }
233 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/data/country-code.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { CountryISO } from '../enums/country-iso.enum';
3 |
4 | @Injectable()
5 | export class CountryCode {
6 | public allCountries = [
7 | ['Afghanistan (افغانستان)', CountryISO.Afghanistan, '93'],
8 | ['Albania (Shqipëri)', CountryISO.Albania, '355'],
9 | ['Algeria (الجزائر)', CountryISO.Algeria, '213'],
10 | ['American Samoa', 'as', '1', 1, ['684']],
11 | ['Andorra', CountryISO.Andorra, '376'],
12 | ['Angola', CountryISO.Angola, '244'],
13 | ['Anguilla', 'ai', '1', 1, ['264']],
14 | ['Antigua and Barbuda', 'ag', '1', 1, ['268']],
15 | ['Argentina', CountryISO.Argentina, '54'],
16 | ['Armenia (Հայաստան)', CountryISO.Armenia, '374'],
17 | ['Aruba', CountryISO.Aruba, '297'],
18 | ['Australia', CountryISO.Australia, '61', 0],
19 | ['Austria (Österreich)', CountryISO.Austria, '43'],
20 | ['Azerbaijan (Azərbaycan)', CountryISO.Azerbaijan, '994'],
21 | ['Bahamas', 'bs', '1', 1, ['242']],
22 | ['Bahrain (البحرين)', CountryISO.Bahrain, '973'],
23 | ['Bangladesh (বাংলাদেশ)', CountryISO.Bangladesh, '880'],
24 | ['Barbados', 'bb', '1', 1, ['246']],
25 | ['Belarus (Беларусь)', CountryISO.Belarus, '375'],
26 | ['Belgium (België)', CountryISO.Belgium, '32'],
27 | ['Belize', CountryISO.Belize, '501'],
28 | ['Benin (Bénin)', CountryISO.Benin, '229'],
29 | ['Bermuda', 'bm', '1', 1, ['441']],
30 | ['Bhutan (འབྲུག)', CountryISO.Bhutan, '975'],
31 | ['Bolivia', CountryISO.Bolivia, '591'],
32 | ['Bosnia and Herzegovina (Босна и Херцеговина)', CountryISO.BosniaAndHerzegovina, '387'],
33 | ['Botswana', CountryISO.Botswana, '267'],
34 | ['Brazil (Brasil)', CountryISO.Brazil, '55'],
35 | ['British Indian Ocean Territory', CountryISO.BritishIndianOceanTerritory, '246'],
36 | ['British Virgin Islands', 'vg', '1', 1, ['284']],
37 | ['Brunei', CountryISO.Brunei, '673'],
38 | ['Bulgaria (България)', CountryISO.Bulgaria, '359'],
39 | ['Burkina Faso', CountryISO.BurkinaFaso, '226'],
40 | ['Burundi (Uburundi)', CountryISO.Burundi, '257'],
41 | ['Cambodia (កម្ពុជា)', CountryISO.Cambodia, '855'],
42 | ['Cameroon (Cameroun)', CountryISO.Cameroon, '237'],
43 | [
44 | 'Canada',
45 | CountryISO.Canada,
46 | '1',
47 | 1,
48 | [
49 | '204',
50 | '226',
51 | '236',
52 | '249',
53 | '250',
54 | '289',
55 | '306',
56 | '343',
57 | '365',
58 | '387',
59 | '403',
60 | '416',
61 | '418',
62 | '431',
63 | '437',
64 | '438',
65 | '450',
66 | '506',
67 | '514',
68 | '519',
69 | '548',
70 | '579',
71 | '581',
72 | '587',
73 | '604',
74 | '613',
75 | '639',
76 | '647',
77 | '672',
78 | '705',
79 | '709',
80 | '742',
81 | '778',
82 | '780',
83 | '782',
84 | '807',
85 | '819',
86 | '825',
87 | '867',
88 | '873',
89 | '902',
90 | '905',
91 | ],
92 | ],
93 | ['Cape Verde (Kabu Verdi)', CountryISO.CapeVerde, '238'],
94 | ['Caribbean Netherlands', CountryISO.CaribbeanNetherlands, '599', 1],
95 | ['Cayman Islands', 'ky', '1', 1, ['345']],
96 | [
97 | 'Central African Republic (République centrafricaine)',
98 | CountryISO.CentralAfricanRepublic,
99 | '236',
100 | ],
101 | ['Chad (Tchad)', CountryISO.Chad, '235'],
102 | ['Chile', CountryISO.Chile, '56'],
103 | ['China (中国)', CountryISO.China, '86'],
104 | ['Christmas Island', CountryISO.ChristmasIsland, '61', 2],
105 | ['Cocos (Keeling) Islands', CountryISO.Cocos, '61', 1],
106 | ['Colombia', CountryISO.Colombia, '57'],
107 | ['Comoros (جزر القمر)', CountryISO.Comoros, '269'],
108 | [
109 | 'Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)',
110 | CountryISO.CongoDRCJamhuriYaKidemokrasiaYaKongo,
111 | '243',
112 | ],
113 | ['Congo (Republic) (Congo-Brazzaville)', CountryISO.CongoRepublicCongoBrazzaville, '242'],
114 | ['Cook Islands', CountryISO.CookIslands, '682'],
115 | ['Costa Rica', CountryISO.CostaRica, '506'],
116 | ['Côte d’Ivoire', CountryISO.CôteDIvoire, '225'],
117 | ['Croatia (Hrvatska)', CountryISO.Croatia, '385'],
118 | ['Cuba', CountryISO.Cuba, '53'],
119 | ['Curaçao', CountryISO.Curaçao, '599', 0],
120 | ['Cyprus (Κύπρος)', CountryISO.Cyprus, '357'],
121 | ['Czech Republic (Česká republika)', CountryISO.CzechRepublic, '420'],
122 | ['Denmark (Danmark)', CountryISO.Denmark, '45'],
123 | ['Djibouti', CountryISO.Djibouti, '253'],
124 | ['Dominica', CountryISO.Dominica, '1767'],
125 | [
126 | 'Dominican Republic (República Dominicana)',
127 | CountryISO.DominicanRepublic,
128 | '1',
129 | 2,
130 | ['809', '829', '849'],
131 | ],
132 | ['Ecuador', CountryISO.Ecuador, '593'],
133 | ['Egypt (مصر)', CountryISO.Egypt, '20'],
134 | ['El Salvador', CountryISO.ElSalvador, '503'],
135 | ['Equatorial Guinea (Guinea Ecuatorial)', CountryISO.EquatorialGuinea, '240'],
136 | ['Eritrea', CountryISO.Eritrea, '291'],
137 | ['Estonia (Eesti)', CountryISO.Estonia, '372'],
138 | ['Ethiopia', CountryISO.Ethiopia, '251'],
139 | ['Falkland Islands (Islas Malvinas)', CountryISO.FalklandIslands, '500'],
140 | ['Faroe Islands (Føroyar)', CountryISO.FaroeIslands, '298'],
141 | ['Fiji', CountryISO.Fiji, '679'],
142 | ['Finland (Suomi)', CountryISO.Finland, '358', 0],
143 | ['France', CountryISO.France, '33'],
144 | ['French Guiana (Guyane française)', CountryISO.FrenchGuiana, '594'],
145 | ['French Polynesia (Polynésie française)', CountryISO.FrenchPolynesia, '689'],
146 | ['Gabon', CountryISO.Gabon, '241'],
147 | ['Gambia', CountryISO.Gambia, '220'],
148 | ['Georgia (საქართველო)', CountryISO.Georgia, '995'],
149 | ['Germany (Deutschland)', CountryISO.Germany, '49'],
150 | ['Ghana (Gaana)', CountryISO.Ghana, '233'],
151 | ['Gibraltar', CountryISO.Gibraltar, '350'],
152 | ['Greece (Ελλάδα)', CountryISO.Greece, '30'],
153 | ['Greenland (Kalaallit Nunaat)', CountryISO.Greenland, '299'],
154 | ['Grenada', CountryISO.Grenada, '1473'],
155 | ['Guadeloupe', CountryISO.Guadeloupe, '590', 0],
156 | ['Guam', 'gu', '1', 1, ['671']],
157 | ['Guatemala', CountryISO.Guatemala, '502'],
158 | ['Guernsey', CountryISO.Guernsey, '44', 1, [1481]],
159 | ['Guinea (Guinée)', CountryISO.Guinea, '224'],
160 | ['Guinea-Bissau (Guiné Bissau)', CountryISO.GuineaBissau, '245'],
161 | ['Guyana', CountryISO.Guyana, '592'],
162 | ['Haiti', CountryISO.Haiti, '509'],
163 | ['Honduras', CountryISO.Honduras, '504'],
164 | ['Hong Kong (香港)', CountryISO.HongKong, '852'],
165 | ['Hungary (Magyarország)', CountryISO.Hungary, '36'],
166 | ['Iceland (Ísland)', CountryISO.Iceland, '354'],
167 | ['India (भारत)', CountryISO.India, '91'],
168 | ['Indonesia', CountryISO.Indonesia, '62'],
169 | ['Iran (ایران)', CountryISO.Iran, '98'],
170 | ['Iraq (العراق)', CountryISO.Iraq, '964'],
171 | ['Ireland', CountryISO.Ireland, '353'],
172 | ['Isle of Man', CountryISO.IsleOfMan, '44', 2, [1624]],
173 | ['Israel (ישראל)', CountryISO.Israel, '972'],
174 | ['Italy (Italia)', CountryISO.Italy, '39', 0],
175 | ['Jamaica', 'jm', '1', 1, ['876']],
176 | ['Japan (日本)', CountryISO.Japan, '81'],
177 | ['Jersey', CountryISO.Jersey, '44', 3, [1534]],
178 | ['Jordan (الأردن)', CountryISO.Jordan, '962'],
179 | ['Kazakhstan (Казахстан)', CountryISO.Kazakhstan, '7', 1],
180 | ['Kenya', CountryISO.Kenya, '254'],
181 | ['Kiribati', CountryISO.Kiribati, '686'],
182 | ['Kosovo', CountryISO.Kosovo, '383'],
183 | ['Kuwait (الكويت)', CountryISO.Kuwait, '965'],
184 | ['Kyrgyzstan (Кыргызстан)', CountryISO.Kyrgyzstan, '996'],
185 | ['Laos (ລາວ)', CountryISO.Laos, '856'],
186 | ['Latvia (Latvija)', CountryISO.Latvia, '371'],
187 | ['Lebanon (لبنان)', CountryISO.Lebanon, '961'],
188 | ['Lesotho', CountryISO.Lesotho, '266'],
189 | ['Liberia', CountryISO.Liberia, '231'],
190 | ['Libya (ليبيا)', CountryISO.Libya, '218'],
191 | ['Liechtenstein', CountryISO.Liechtenstein, '423'],
192 | ['Lithuania (Lietuva)', CountryISO.Lithuania, '370'],
193 | ['Luxembourg', CountryISO.Luxembourg, '352'],
194 | ['Macau (澳門)', CountryISO.Macau, '853'],
195 | ['Macedonia (FYROM) (Македонија)', CountryISO.Macedonia, '389'],
196 | ['Madagascar (Madagasikara)', CountryISO.Madagascar, '261'],
197 | ['Malawi', CountryISO.Malawi, '265'],
198 | ['Malaysia', CountryISO.Malaysia, '60'],
199 | ['Maldives', CountryISO.Maldives, '960'],
200 | ['Mali', CountryISO.Mali, '223'],
201 | ['Malta', CountryISO.Malta, '356'],
202 | ['Marshall Islands', CountryISO.MarshallIslands, '692'],
203 | ['Martinique', CountryISO.Martinique, '596'],
204 | ['Mauritania (موريتانيا)', CountryISO.Mauritania, '222'],
205 | ['Mauritius (Moris)', CountryISO.Mauritius, '230'],
206 | ['Mayotte', CountryISO.Mayotte, '262', 1],
207 | ['Mexico (México)', CountryISO.Mexico, '52'],
208 | ['Micronesia', CountryISO.Micronesia, '691'],
209 | ['Moldova (Republica Moldova)', CountryISO.Moldova, '373'],
210 | ['Monaco', CountryISO.Monaco, '377'],
211 | ['Mongolia (Монгол)', CountryISO.Mongolia, '976'],
212 | ['Montenegro (Crna Gora)', CountryISO.Montenegro, '382'],
213 | ['Montserrat', 'ms', '1', 1, ['664']],
214 | ['Morocco (المغرب)', CountryISO.Morocco, '212', 0],
215 | ['Mozambique (Moçambique)', CountryISO.Mozambique, '258'],
216 | ['Myanmar (Burma) (မြန်မာ)', CountryISO.Myanmar, '95'],
217 | ['Namibia (Namibië)', CountryISO.Namibia, '264'],
218 | ['Nauru', CountryISO.Nauru, '674'],
219 | ['Nepal (नेपाल)', CountryISO.Nepal, '977'],
220 | ['Netherlands (Nederland)', CountryISO.Netherlands, '31'],
221 | ['New Caledonia (Nouvelle-Calédonie)', CountryISO.NewCaledonia, '687'],
222 | ['New Zealand', CountryISO.NewZealand, '64'],
223 | ['Nicaragua', CountryISO.Nicaragua, '505'],
224 | ['Niger (Nijar)', CountryISO.Niger, '227'],
225 | ['Nigeria', CountryISO.Nigeria, '234'],
226 | ['Niue', CountryISO.Niue, '683'],
227 | ['Norfolk Island', CountryISO.NorfolkIsland, '672'],
228 | ['North Korea (조선 민주주의 인민 공화국)', CountryISO.NorthKorea, '850'],
229 | ['Northern Mariana Islands', CountryISO.NorthernMarianaIslands, '1670'],
230 | ['Norway (Norge)', CountryISO.Norway, '47', 0],
231 | ['Oman (عُمان)', CountryISO.Oman, '968'],
232 | ['Pakistan (پاکستان)', CountryISO.Pakistan, '92'],
233 | ['Palau', CountryISO.Palau, '680'],
234 | ['Palestine (فلسطين)', CountryISO.Palestine, '970'],
235 | ['Panama (Panamá)', CountryISO.Panama, '507'],
236 | ['Papua New Guinea', CountryISO.PapuaNewGuinea, '675'],
237 | ['Paraguay', CountryISO.Paraguay, '595'],
238 | ['Peru (Perú)', CountryISO.Peru, '51'],
239 | ['Philippines', CountryISO.Philippines, '63'],
240 | ['Poland (Polska)', CountryISO.Poland, '48'],
241 | ['Portugal', CountryISO.Portugal, '351'],
242 | ['Puerto Rico', CountryISO.PuertoRico, '1', 3, ['787', '939']],
243 | ['Qatar (قطر)', CountryISO.Qatar, '974'],
244 | ['Réunion (La Réunion)', CountryISO.Réunion, '262', 0],
245 | ['Romania (România)', CountryISO.Romania, '40'],
246 | ['Russia (Россия)', CountryISO.Russia, '7', 0],
247 | ['Rwanda', CountryISO.Rwanda, '250'],
248 | ['Saint Barthélemy (Saint-Barthélemy)', CountryISO.SaintBarthélemy, '590', 1],
249 | ['Saint Helena', CountryISO.SaintHelena, '290'],
250 | ['Saint Kitts and Nevis', CountryISO.SaintKittsAndNevis, '1869'],
251 | ['Saint Lucia', 'lc', '1', 1, ['758']],
252 | ['Saint Martin (Saint-Martin (partie française))', CountryISO.SaintMartin, '590', 2],
253 | [
254 | 'Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)',
255 | CountryISO.SaintPierreAndMiquelon,
256 | '508',
257 | ],
258 | ['Saint Vincent and the Grenadines', 'vc', '1', 1, ['784']],
259 | ['Samoa', CountryISO.Samoa, '685'],
260 | ['San Marino', CountryISO.SanMarino, '378'],
261 | ['São Tomé and Príncipe (São Tomé e Príncipe)', CountryISO.SãoToméAndPríncipe, '239'],
262 | ['Saudi Arabia (المملكة العربية السعودية)', CountryISO.SaudiArabia, '966'],
263 | ['Senegal (Sénégal)', CountryISO.Senegal, '221'],
264 | ['Serbia (Србија)', CountryISO.Serbia, '381'],
265 | ['Seychelles', CountryISO.Seychelles, '248'],
266 | ['Sierra Leone', CountryISO.SierraLeone, '232'],
267 | ['Singapore', CountryISO.Singapore, '65'],
268 | ['Sint Maarten', 'sx', '1', 1, ['721']],
269 | ['Slovakia (Slovensko)', CountryISO.Slovakia, '421'],
270 | ['Slovenia (Slovenija)', CountryISO.Slovenia, '386'],
271 | ['Solomon Islands', CountryISO.SolomonIslands, '677'],
272 | ['Somalia (Soomaaliya)', CountryISO.Somalia, '252'],
273 | ['South Africa', CountryISO.SouthAfrica, '27'],
274 | ['South Korea (대한민국)', CountryISO.SouthKorea, '82'],
275 | ['South Sudan (جنوب السودان)', CountryISO.SouthSudan, '211'],
276 | ['Spain (España)', CountryISO.Spain, '34'],
277 | ['Sri Lanka (ශ්රී ලංකාව)', CountryISO.SriLanka, '94'],
278 | ['Sudan (السودان)', CountryISO.Sudan, '249'],
279 | ['Suriname', CountryISO.Suriname, '597'],
280 | ['Svalbard and Jan Mayen', CountryISO.SvalbardAndJanMayen, '47', 1],
281 | ['Swaziland', CountryISO.Swaziland, '268'],
282 | ['Sweden (Sverige)', CountryISO.Sweden, '46'],
283 | ['Switzerland (Schweiz)', CountryISO.Switzerland, '41'],
284 | ['Syria (سوريا)', CountryISO.Syria, '963'],
285 | ['Taiwan (台灣)', CountryISO.Taiwan, '886'],
286 | ['Tajikistan', CountryISO.Tajikistan, '992'],
287 | ['Tanzania', CountryISO.Tanzania, '255'],
288 | ['Thailand (ไทย)', CountryISO.Thailand, '66'],
289 | ['Timor-Leste', CountryISO.TimorLeste, '670'],
290 | ['Togo', CountryISO.Togo, '228'],
291 | ['Tokelau', CountryISO.Tokelau, '690'],
292 | ['Tonga', CountryISO.Tonga, '676'],
293 | ['Trinidad and Tobago', 'tt', '1', 1, ['868']],
294 | ['Tunisia (تونس)', CountryISO.Tunisia, '216'],
295 | ['Turkey (Türkiye)', CountryISO.Turkey, '90'],
296 | ['Turkmenistan', CountryISO.Turkmenistan, '993'],
297 | ['Turks and Caicos Islands', CountryISO.TurksAndCaicosIslands, '1649'],
298 | ['Tuvalu', CountryISO.Tuvalu, '688'],
299 | ['U.S. Virgin Islands', 'vi', '1', 1, ['340']],
300 | ['Uganda', CountryISO.Uganda, '256'],
301 | ['Ukraine (Україна)', CountryISO.Ukraine, '380'],
302 | ['United Arab Emirates (الإمارات العربية المتحدة)', CountryISO.UnitedArabEmirates, '971'],
303 | ['United Kingdom', CountryISO.UnitedKingdom, '44', 0],
304 | ['United States', CountryISO.UnitedStates, '1', 0],
305 | ['Uruguay', CountryISO.Uruguay, '598'],
306 | ['Uzbekistan (Oʻzbekiston)', CountryISO.Uzbekistan, '998'],
307 | ['Vanuatu', CountryISO.Vanuatu, '678'],
308 | ['Vatican City (Città del Vaticano)', CountryISO.VaticanCity, '39', 1],
309 | ['Venezuela', CountryISO.Venezuela, '58'],
310 | ['Vietnam (Việt Nam)', CountryISO.Vietnam, '84'],
311 | ['Wallis and Futuna', CountryISO.WallisAndFutuna, '681'],
312 | ['Western Sahara (الصحراء الغربية)', CountryISO.WesternSahara, '212', 1],
313 | ['Yemen (اليمن)', CountryISO.Yemen, '967'],
314 | ['Zambia', CountryISO.Zambia, '260'],
315 | ['Zimbabwe', CountryISO.Zimbabwe, '263'],
316 | ['Åland Islands', CountryISO.ÅlandIslands, '358', 1],
317 | ];
318 | }
319 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/directives/native-element-injector.directive.ts:
--------------------------------------------------------------------------------
1 | import { Directive, ElementRef, OnInit } from '@angular/core';
2 | import { NgControl } from '@angular/forms';
3 |
4 | /*
5 | "Property 'nativeElement' does not exist on type 'FormControl'".
6 | 'NativeElementInjectorDirective' injects nativeElement to each control,
7 | so we can access it from inside validator for example.
8 | More about this approach and reasons for this:
9 | https://github.com/angular/angular/issues/18025
10 | https://stackoverflow.com/a/54075119/1617590
11 | */
12 | @Directive({
13 | // tslint:disable-next-line: directive-selector
14 | selector: '[ngModel], [formControl], [formControlName]',
15 | })
16 | export class NativeElementInjectorDirective implements OnInit {
17 | constructor(private controlDir: NgControl, private host: ElementRef) {}
18 | ngOnInit() {
19 | if (this.controlDir.control) {
20 | // @ts-ignore
21 | this.controlDir.control['nativeElement'] = this.host.nativeElement;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/enums/country-iso.enum.ts:
--------------------------------------------------------------------------------
1 | export enum CountryISO {
2 | Afghanistan = 'af',
3 | Albania = 'al',
4 | Algeria = 'dz',
5 | AmericanSamoa = 'as',
6 | Andorra = 'ad',
7 | Angola = 'ao',
8 | Anguilla = 'ai',
9 | AntiguaAndBarbuda = 'ag',
10 | Argentina = 'ar',
11 | Armenia = 'am',
12 | Aruba = 'aw',
13 | Australia = 'au',
14 | Austria = 'at',
15 | Azerbaijan = 'az',
16 | Bahamas = 'bs',
17 | Bahrain = 'bh',
18 | Bangladesh = 'bd',
19 | Barbados = 'bb',
20 | Belarus = 'by',
21 | Belgium = 'be',
22 | Belize = 'bz',
23 | Benin = 'bj',
24 | Bermuda = 'bm',
25 | Bhutan = 'bt',
26 | Bolivia = 'bo',
27 | BosniaAndHerzegovina = 'ba',
28 | Botswana = 'bw',
29 | Brazil = 'br',
30 | BritishIndianOceanTerritory = 'io',
31 | BritishVirginIslands = 'vg',
32 | Brunei = 'bn',
33 | Bulgaria = 'bg',
34 | BurkinaFaso = 'bf',
35 | Burundi = 'bi',
36 | Cambodia = 'kh',
37 | Cameroon = 'cm',
38 | Canada = 'ca',
39 | CapeVerde = 'cv',
40 | CaribbeanNetherlands = 'bq',
41 | CaymanIslands = 'ky',
42 | CentralAfricanRepublic = 'cf',
43 | Chad = 'td',
44 | Chile = 'cl',
45 | China = 'cn',
46 | ChristmasIsland = 'cx',
47 | Cocos = 'cc',
48 | Colombia = 'co',
49 | Comoros = 'km',
50 | CongoDRCJamhuriYaKidemokrasiaYaKongo = 'cd',
51 | CongoRepublicCongoBrazzaville = 'cg',
52 | CookIslands = 'ck',
53 | CostaRica = 'cr',
54 | CôteDIvoire = 'ci',
55 | Croatia = 'hr',
56 | Cuba = 'cu',
57 | Curaçao = 'cw',
58 | Cyprus = 'cy',
59 | CzechRepublic = 'cz',
60 | Denmark = 'dk',
61 | Djibouti = 'dj',
62 | Dominica = 'dm',
63 | DominicanRepublic = 'do',
64 | Ecuador = 'ec',
65 | Egypt = 'eg',
66 | ElSalvador = 'sv',
67 | EquatorialGuinea = 'gq',
68 | Eritrea = 'er',
69 | Estonia = 'ee',
70 | Ethiopia = 'et',
71 | FalklandIslands = 'fk',
72 | FaroeIslands = 'fo',
73 | Fiji = 'fj',
74 | Finland = 'fi',
75 | France = 'fr',
76 | FrenchGuiana = 'gf',
77 | FrenchPolynesia = 'pf',
78 | Gabon = 'ga',
79 | Gambia = 'gm',
80 | Georgia = 'ge',
81 | Germany = 'de',
82 | Ghana = 'gh',
83 | Gibraltar = 'gi',
84 | Greece = 'gr',
85 | Greenland = 'gl',
86 | Grenada = 'gd',
87 | Guadeloupe = 'gp',
88 | Guam = 'gu',
89 | Guatemala = 'gt',
90 | Guernsey = 'gg',
91 | Guinea = 'gn',
92 | GuineaBissau = 'gw',
93 | Guyana = 'gy',
94 | Haiti = 'ht',
95 | Honduras = 'hn',
96 | HongKong = 'hk',
97 | Hungary = 'hu',
98 | Iceland = 'is',
99 | India = 'in',
100 | Indonesia = 'id',
101 | Iran = 'ir',
102 | Iraq = 'iq',
103 | Ireland = 'ie',
104 | IsleOfMan = 'im',
105 | Israel = 'il',
106 | Italy = 'it',
107 | Jamaica = 'jm',
108 | Japan = 'jp',
109 | Jersey = 'je',
110 | Jordan = 'jo',
111 | Kazakhstan = 'kz',
112 | Kenya = 'ke',
113 | Kiribati = 'ki',
114 | Kosovo = 'xk',
115 | Kuwait = 'kw',
116 | Kyrgyzstan = 'kg',
117 | Laos = 'la',
118 | Latvia = 'lv',
119 | Lebanon = 'lb',
120 | Lesotho = 'ls',
121 | Liberia = 'lr',
122 | Libya = 'ly',
123 | Liechtenstein = 'li',
124 | Lithuania = 'lt',
125 | Luxembourg = 'lu',
126 | Macau = 'mo',
127 | Macedonia = 'mk',
128 | Madagascar = 'mg',
129 | Malawi = 'mw',
130 | Malaysia = 'my',
131 | Maldives = 'mv',
132 | Mali = 'ml',
133 | Malta = 'mt',
134 | MarshallIslands = 'mh',
135 | Martinique = 'mq',
136 | Mauritania = 'mr',
137 | Mauritius = 'mu',
138 | Mayotte = 'yt',
139 | Mexico = 'mx',
140 | Micronesia = 'fm',
141 | Moldova = 'md',
142 | Monaco = 'mc',
143 | Mongolia = 'mn',
144 | Montenegro = 'me',
145 | Montserrat = 'ms',
146 | Morocco = 'ma',
147 | Mozambique = 'mz',
148 | Myanmar = 'mm',
149 | Namibia = 'na',
150 | Nauru = 'nr',
151 | Nepal = 'np',
152 | Netherlands = 'nl',
153 | NewCaledonia = 'nc',
154 | NewZealand = 'nz',
155 | Nicaragua = 'ni',
156 | Niger = 'ne',
157 | Nigeria = 'ng',
158 | Niue = 'nu',
159 | NorfolkIsland = 'nf',
160 | NorthKorea = 'kp',
161 | NorthernMarianaIslands = 'mp',
162 | Norway = 'no',
163 | Oman = 'om',
164 | Pakistan = 'pk',
165 | Palau = 'pw',
166 | Palestine = 'ps',
167 | Panama = 'pa',
168 | PapuaNewGuinea = 'pg',
169 | Paraguay = 'py',
170 | Peru = 'pe',
171 | Philippines = 'ph',
172 | Poland = 'pl',
173 | Portugal = 'pt',
174 | PuertoRico = 'pr',
175 | Qatar = 'qa',
176 | Réunion = 're',
177 | Romania = 'ro',
178 | Russia = 'ru',
179 | Rwanda = 'rw',
180 | SaintBarthélemy = 'bl',
181 | SaintHelena = 'sh',
182 | SaintKittsAndNevis = 'kn',
183 | SaintLucia = 'lc',
184 | SaintMartin = 'mf',
185 | SaintPierreAndMiquelon = 'pm',
186 | SaintVincentAndTheGrenadines = 'vc',
187 | Samoa = 'ws',
188 | SanMarino = 'sm',
189 | SãoToméAndPríncipe = 'st',
190 | SaudiArabia = 'sa',
191 | Senegal = 'sn',
192 | Serbia = 'rs',
193 | Seychelles = 'sc',
194 | SierraLeone = 'sl',
195 | Singapore = 'sg',
196 | SintMaarten = 'sx',
197 | Slovakia = 'sk',
198 | Slovenia = 'si',
199 | SolomonIslands = 'sb',
200 | Somalia = 'so',
201 | SouthAfrica = 'za',
202 | SouthKorea = 'kr',
203 | SouthSudan = 'ss',
204 | Spain = 'es',
205 | SriLanka = 'lk',
206 | Sudan = 'sd',
207 | Suriname = 'sr',
208 | SvalbardAndJanMayen = 'sj',
209 | Swaziland = 'sz',
210 | Sweden = 'se',
211 | Switzerland = 'ch',
212 | Syria = 'sy',
213 | Taiwan = 'tw',
214 | Tajikistan = 'tj',
215 | Tanzania = 'tz',
216 | Thailand = 'th',
217 | TimorLeste = 'tl',
218 | Togo = 'tg',
219 | Tokelau = 'tk',
220 | Tonga = 'to',
221 | TrinidadAndTobago = 'tt',
222 | Tunisia = 'tn',
223 | Turkey = 'tr',
224 | Turkmenistan = 'tm',
225 | TurksAndCaicosIslands = 'tc',
226 | Tuvalu = 'tv',
227 | USVirginIslands = 'vi',
228 | Uganda = 'ug',
229 | Ukraine = 'ua',
230 | UnitedArabEmirates = 'ae',
231 | UnitedKingdom = 'gb',
232 | UnitedStates = 'us',
233 | Uruguay = 'uy',
234 | Uzbekistan = 'uz',
235 | Vanuatu = 'vu',
236 | VaticanCity = 'va',
237 | Venezuela = 've',
238 | Vietnam = 'vn',
239 | WallisAndFutuna = 'wf',
240 | WesternSahara = 'eh',
241 | Yemen = 'ye',
242 | Zambia = 'zm',
243 | Zimbabwe = 'zw',
244 | ÅlandIslands = 'ax',
245 | }
246 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/enums/phone-number-format.enum.ts:
--------------------------------------------------------------------------------
1 | export enum PhoneNumberFormat {
2 | International = 'INTERNATIONAL',
3 | National = 'NATIONAL',
4 | }
5 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/enums/search-country-field.enum.ts:
--------------------------------------------------------------------------------
1 | export enum SearchCountryField {
2 | DialCode = 'dialCode',
3 | Iso2 = 'iso2',
4 | Name = 'name',
5 | All = 'all',
6 | }
7 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/interfaces/change-data.ts:
--------------------------------------------------------------------------------
1 | export interface ChangeData {
2 | number?: string;
3 | internationalNumber?: string;
4 | nationalNumber?: string;
5 | e164Number?: string;
6 | countryCode?: string;
7 | dialCode?: string;
8 | }
9 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/model/country.model.ts:
--------------------------------------------------------------------------------
1 | export interface Country {
2 | name: string;
3 | iso2: string;
4 | dialCode: string;
5 | priority: number;
6 | areaCodes?: string[];
7 | htmlId: string;
8 | flagClass: string;
9 | placeHolder: string;
10 | }
11 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.component.css:
--------------------------------------------------------------------------------
1 | li.iti__country:hover {
2 | background-color: rgba(0, 0, 0, 0.05);
3 | }
4 | .iti__selected-flag.dropdown-toggle:after {
5 | content: none;
6 | }
7 | .iti__flag-container.disabled {
8 | cursor: default !important;
9 | }
10 | .iti.iti--allow-dropdown .flag-container.disabled:hover .iti__selected-flag {
11 | background: none;
12 | }
13 | .iti__dropdown-content {
14 | border: 1px solid #ccc;
15 | width: fit-content;
16 | padding: 1px;
17 | border-collapse: collapse;
18 | }
19 | .search-container {
20 | position: relative;
21 | }
22 | .search-container input {
23 | width: 100%;
24 | border: none;
25 | border-bottom: 1px solid #ccc;
26 | padding-left: 10px;
27 | }
28 | .search-icon {
29 | position: absolute;
30 | z-index: 2;
31 | width: 25px;
32 | margin: 1px 10px;
33 | }
34 | .iti__country-list {
35 | position: relative;
36 | border: none;
37 | /* max-height: 170px; */
38 | }
39 |
40 | .iti input#country-search-box {
41 | padding-left: 6px;
42 | }
43 |
44 | .iti .selected-dial-code {
45 | margin-left: 6px;
46 | }
47 |
48 | .iti.separate-dial-code .iti__selected-flag,
49 | .iti.separate-dial-code.iti--allow-dropdown.iti-sdc-2 .iti__selected-flag,
50 | .iti.separate-dial-code.iti--allow-dropdown.iti-sdc-3 .iti__selected-flag,
51 | .iti.separate-dial-code.iti--allow-dropdown.iti-sdc-4 .iti__selected-flag {
52 | width: 93px;
53 | }
54 |
55 | .iti.separate-dial-code input,
56 | .iti.separate-dial-code.iti--allow-dropdown.iti-sdc-2 input,
57 | .iti.separate-dial-code.iti--allow-dropdown.iti-sdc-3 input,
58 | .iti.separate-dial-code.iti--allow-dropdown.iti-sdc-4 input {
59 | padding-left: 98px;
60 | }
61 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.component.html:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
+{{ selectedCountry.dialCode }}
11 |
12 |
13 |
52 |
53 |
69 |
70 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { NgxIntlTelInputComponent } from './ngx-intl-tel-input.component';
4 |
5 | describe('NgxIntlTelInputComponent', () => {
6 | let component: NgxIntlTelInputComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [NgxIntlTelInputComponent],
12 | }).compileComponents();
13 | }));
14 |
15 | beforeEach(() => {
16 | fixture = TestBed.createComponent(NgxIntlTelInputComponent);
17 | component = fixture.componentInstance;
18 | fixture.detectChanges();
19 | });
20 |
21 | it('should create', () => {
22 | expect(component).toBeTruthy();
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.component.ts:
--------------------------------------------------------------------------------
1 | import * as lpn from 'google-libphonenumber';
2 |
3 | import {
4 | Component,
5 | ElementRef,
6 | EventEmitter,
7 | forwardRef,
8 | Input,
9 | OnChanges,
10 | OnInit,
11 | Output,
12 | SimpleChanges,
13 | ViewChild,
14 | } from '@angular/core';
15 | import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms';
16 |
17 | import { setTheme } from 'ngx-bootstrap/utils';
18 |
19 | import { CountryCode } from './data/country-code';
20 | import { CountryISO } from './enums/country-iso.enum';
21 | import { SearchCountryField } from './enums/search-country-field.enum';
22 | import { ChangeData } from './interfaces/change-data';
23 | import { Country } from './model/country.model';
24 | import { phoneNumberValidator } from './ngx-intl-tel-input.validator';
25 | import { PhoneNumberFormat } from './enums/phone-number-format.enum';
26 |
27 | @Component({
28 | // tslint:disable-next-line: component-selector
29 | selector: 'ngx-intl-tel-input',
30 | templateUrl: './ngx-intl-tel-input.component.html',
31 | styleUrls: ['./bootstrap-dropdown.css', './ngx-intl-tel-input.component.css'],
32 | providers: [
33 | CountryCode,
34 | {
35 | provide: NG_VALUE_ACCESSOR,
36 | // tslint:disable-next-line:no-forward-ref
37 | useExisting: forwardRef(() => NgxIntlTelInputComponent),
38 | multi: true,
39 | },
40 | {
41 | provide: NG_VALIDATORS,
42 | useValue: phoneNumberValidator,
43 | multi: true,
44 | },
45 | ],
46 | })
47 | export class NgxIntlTelInputComponent implements OnInit, OnChanges {
48 | @Input() value: string | undefined = '';
49 | @Input() preferredCountries: Array = [];
50 | @Input() enablePlaceholder = true;
51 | @Input() customPlaceholder: string;
52 | @Input() numberFormat: PhoneNumberFormat = PhoneNumberFormat.International;
53 | @Input() cssClass = 'form-control';
54 | @Input() onlyCountries: Array = [];
55 | @Input() enableAutoCountrySelect = true;
56 | @Input() searchCountryFlag = false;
57 | @Input() searchCountryField: SearchCountryField[] = [SearchCountryField.All];
58 | @Input() searchCountryPlaceholder = 'Search Country';
59 | @Input() maxLength: number;
60 | @Input() selectFirstCountry = true;
61 | @Input() selectedCountryISO: CountryISO;
62 | @Input() phoneValidation = true;
63 | @Input() inputId = 'phone';
64 | @Input() separateDialCode = false;
65 | separateDialCodeClass: string;
66 |
67 | @Output() readonly countryChange = new EventEmitter();
68 |
69 | selectedCountry: Country = {
70 | areaCodes: undefined,
71 | dialCode: '',
72 | htmlId: '',
73 | flagClass: '',
74 | iso2: '',
75 | name: '',
76 | placeHolder: '',
77 | priority: 0,
78 | };
79 |
80 | phoneNumber: string | undefined = '';
81 | allCountries: Array = [];
82 | preferredCountriesInDropDown: Array = [];
83 | // Has to be 'any' to prevent a need to install @types/google-libphonenumber by the package user...
84 | phoneUtil: any = lpn.PhoneNumberUtil.getInstance();
85 | disabled = false;
86 | errors: Array = ['Phone number is required.'];
87 | countrySearchText = '';
88 |
89 | @ViewChild('countryList') countryList: ElementRef;
90 |
91 | onTouched = () => {};
92 | propagateChange = (_: ChangeData) => {};
93 |
94 | constructor(private countryCodeData: CountryCode) {
95 | // If this is not set, ngx-bootstrap will try to use the bs3 CSS (which is not what we've embedded) and will
96 | // Add the wrong classes and such
97 | setTheme('bs4');
98 | }
99 |
100 | ngOnInit() {
101 | this.init();
102 | }
103 |
104 | ngOnChanges(changes: SimpleChanges) {
105 | const selectedISO = changes['selectedCountryISO'];
106 | if (
107 | this.allCountries &&
108 | selectedISO &&
109 | selectedISO.currentValue !== selectedISO.previousValue
110 | ) {
111 | this.updateSelectedCountry();
112 | }
113 | if (changes['preferredCountries']) {
114 | this.updatePreferredCountries();
115 | }
116 | this.checkSeparateDialCodeStyle();
117 | }
118 |
119 | /*
120 | This is a wrapper method to avoid calling this.ngOnInit() in writeValue().
121 | Ref: http://codelyzer.com/rules/no-life-cycle-call/
122 | */
123 | init() {
124 | this.fetchCountryData();
125 | if (this.preferredCountries.length) {
126 | this.updatePreferredCountries();
127 | }
128 | if (this.onlyCountries.length) {
129 | this.allCountries = this.allCountries.filter(c => this.onlyCountries.includes(c.iso2));
130 | }
131 | if (this.selectFirstCountry) {
132 | if (this.preferredCountriesInDropDown.length) {
133 | this.setSelectedCountry(this.preferredCountriesInDropDown[0]);
134 | } else {
135 | this.setSelectedCountry(this.allCountries[0]);
136 | }
137 | }
138 | this.updateSelectedCountry();
139 | this.checkSeparateDialCodeStyle();
140 | }
141 |
142 | setSelectedCountry(country: Country) {
143 | this.selectedCountry = country;
144 | this.countryChange.emit(country);
145 | }
146 |
147 | /**
148 | * Search country based on country name, iso2, dialCode or all of them.
149 | */
150 | public searchCountry() {
151 | if (!this.countrySearchText) {
152 | this.countryList.nativeElement.querySelector('.iti__country-list li').scrollIntoView({
153 | behavior: 'smooth',
154 | block: 'nearest',
155 | inline: 'nearest',
156 | });
157 | return;
158 | }
159 | const countrySearchTextLower = this.countrySearchText.toLowerCase();
160 | // @ts-ignore
161 | const country = this.allCountries.filter(c => {
162 | if (this.searchCountryField.indexOf(SearchCountryField.All) > -1) {
163 | // Search in all fields
164 | if (c.iso2.toLowerCase().startsWith(countrySearchTextLower)) {
165 | return c;
166 | }
167 | if (c.name.toLowerCase().startsWith(countrySearchTextLower)) {
168 | return c;
169 | }
170 | if (c.dialCode.startsWith(this.countrySearchText)) {
171 | return c;
172 | }
173 | } else {
174 | // Or search by specific SearchCountryField(s)
175 | if (this.searchCountryField.indexOf(SearchCountryField.Iso2) > -1) {
176 | if (c.iso2.toLowerCase().startsWith(countrySearchTextLower)) {
177 | return c;
178 | }
179 | }
180 | if (this.searchCountryField.indexOf(SearchCountryField.Name) > -1) {
181 | if (c.name.toLowerCase().startsWith(countrySearchTextLower)) {
182 | return c;
183 | }
184 | }
185 | if (this.searchCountryField.indexOf(SearchCountryField.DialCode) > -1) {
186 | if (c.dialCode.startsWith(this.countrySearchText)) {
187 | return c;
188 | }
189 | }
190 | }
191 | });
192 |
193 | if (country.length > 0) {
194 | const el = this.countryList.nativeElement.querySelector('#' + country[0].htmlId);
195 | if (el) {
196 | el.scrollIntoView({
197 | behavior: 'smooth',
198 | block: 'nearest',
199 | inline: 'nearest',
200 | });
201 | }
202 | }
203 |
204 | this.checkSeparateDialCodeStyle();
205 | }
206 |
207 | public onPhoneNumberChange(): void {
208 | let countryCode: string | undefined;
209 | // Handle the case where the user sets the value programatically based on a persisted ChangeData obj.
210 | if (this.phoneNumber && typeof this.phoneNumber === 'object') {
211 | const numberObj: ChangeData = this.phoneNumber;
212 | this.phoneNumber = numberObj.number;
213 | countryCode = numberObj.countryCode;
214 | }
215 |
216 | this.value = this.phoneNumber;
217 | countryCode = countryCode || this.selectedCountry.iso2;
218 | // @ts-ignore
219 | const number = this.getParsedNumber(this.phoneNumber, countryCode);
220 |
221 | // auto select country based on the extension (and areaCode if needed) (e.g select Canada if number starts with +1 416)
222 | if (this.enableAutoCountrySelect) {
223 | countryCode =
224 | number && number.getCountryCode()
225 | ? // @ts-ignore
226 | this.getCountryIsoCode(number.getCountryCode(), number)
227 | : this.selectedCountry.iso2;
228 | if (countryCode && countryCode !== this.selectedCountry.iso2) {
229 | const newCountry = this.allCountries
230 | .slice()
231 | .sort((a, b) => {
232 | return a.priority - b.priority;
233 | })
234 | .find(c => c.iso2 === countryCode);
235 | if (newCountry) {
236 | this.selectedCountry = newCountry;
237 | }
238 | }
239 | }
240 | countryCode = countryCode ? countryCode : this.selectedCountry.iso2;
241 |
242 | this.checkSeparateDialCodeStyle();
243 |
244 | if (!this.value) {
245 | // Reason: avoid https://stackoverflow.com/a/54358133/1617590
246 | // tslint:disable-next-line: no-null-keyword
247 | // @ts-ignore
248 | this.propagateChange(null);
249 | } else {
250 | const intlNo = number
251 | ? this.phoneUtil.format(number, lpn.PhoneNumberFormat.INTERNATIONAL)
252 | : '';
253 |
254 | // parse phoneNumber if separate dial code is needed
255 | if (this.separateDialCode && intlNo) {
256 | this.value = this.removeDialCode(intlNo);
257 | }
258 |
259 | this.propagateChange({
260 | number: this.value,
261 | internationalNumber: intlNo,
262 | nationalNumber: number ? this.phoneUtil.format(number, lpn.PhoneNumberFormat.NATIONAL) : '',
263 | e164Number: number ? this.phoneUtil.format(number, lpn.PhoneNumberFormat.E164) : '',
264 | countryCode: countryCode.toUpperCase(),
265 | dialCode: '+' + this.selectedCountry.dialCode,
266 | });
267 | }
268 | }
269 |
270 | public onCountrySelect(country: Country, el: { focus: () => void }): void {
271 | this.setSelectedCountry(country);
272 |
273 | this.checkSeparateDialCodeStyle();
274 |
275 | if (this.phoneNumber && this.phoneNumber.length > 0) {
276 | this.value = this.phoneNumber;
277 | const number = this.getParsedNumber(this.phoneNumber, this.selectedCountry.iso2);
278 | const intlNo = number
279 | ? this.phoneUtil.format(number, lpn.PhoneNumberFormat.INTERNATIONAL)
280 | : '';
281 | // parse phoneNumber if separate dial code is needed
282 | if (this.separateDialCode && intlNo) {
283 | this.value = this.removeDialCode(intlNo);
284 | }
285 |
286 | this.propagateChange({
287 | number: this.value,
288 | internationalNumber: intlNo,
289 | nationalNumber: number ? this.phoneUtil.format(number, lpn.PhoneNumberFormat.NATIONAL) : '',
290 | e164Number: number ? this.phoneUtil.format(number, lpn.PhoneNumberFormat.E164) : '',
291 | countryCode: this.selectedCountry.iso2.toUpperCase(),
292 | dialCode: '+' + this.selectedCountry.dialCode,
293 | });
294 | } else {
295 | // Reason: avoid https://stackoverflow.com/a/54358133/1617590
296 | // tslint:disable-next-line: no-null-keyword
297 | // @ts-ignore
298 | this.propagateChange(null);
299 | }
300 |
301 | el.focus();
302 | }
303 |
304 | public onInputKeyPress(event: KeyboardEvent): void {
305 | const allowedChars = /[0-9\+\-\(\)\ ]/;
306 | const allowedCtrlChars = /[axcv]/; // Allows copy-pasting
307 | const allowedOtherKeys = [
308 | 'ArrowLeft',
309 | 'ArrowUp',
310 | 'ArrowRight',
311 | 'ArrowDown',
312 | 'Home',
313 | 'End',
314 | 'Insert',
315 | 'Delete',
316 | 'Backspace',
317 | ];
318 |
319 | if (
320 | !allowedChars.test(event.key) &&
321 | !(event.ctrlKey && allowedCtrlChars.test(event.key)) &&
322 | !allowedOtherKeys.includes(event.key)
323 | ) {
324 | event.preventDefault();
325 | }
326 | }
327 |
328 | registerOnChange(fn: any): void {
329 | this.propagateChange = fn;
330 | }
331 |
332 | registerOnTouched(fn: any) {
333 | this.onTouched = fn;
334 | }
335 |
336 | setDisabledState(isDisabled: boolean): void {
337 | this.disabled = isDisabled;
338 | }
339 |
340 | writeValue(obj: any): void {
341 | if (obj === undefined) {
342 | this.init();
343 | }
344 | this.phoneNumber = obj;
345 | setTimeout(() => {
346 | this.onPhoneNumberChange();
347 | }, 1);
348 | }
349 |
350 | resolvePlaceholder(): string {
351 | let placeholder = '';
352 | if (this.customPlaceholder) {
353 | placeholder = this.customPlaceholder;
354 | } else if (this.selectedCountry.placeHolder) {
355 | placeholder = this.selectedCountry.placeHolder;
356 | if (this.separateDialCode) {
357 | placeholder = this.removeDialCode(placeholder);
358 | }
359 | }
360 | return placeholder;
361 | }
362 |
363 | /* --------------------------------- Helpers -------------------------------- */
364 | /**
365 | * Returns parse PhoneNumber object.
366 | * @param phoneNumber string
367 | * @param countryCode string
368 | */
369 | private getParsedNumber(phoneNumber: string, countryCode: string): lpn.PhoneNumber {
370 | let number: lpn.PhoneNumber;
371 | try {
372 | number = this.phoneUtil.parse(phoneNumber, countryCode.toUpperCase());
373 | } catch (e) {}
374 | // @ts-ignore
375 | return number;
376 | }
377 |
378 | /**
379 | * Adjusts input alignment based on the dial code presentation style.
380 | */
381 | private checkSeparateDialCodeStyle() {
382 | if (this.separateDialCode && this.selectedCountry) {
383 | const cntryCd = this.selectedCountry.dialCode;
384 | this.separateDialCodeClass = 'separate-dial-code iti-sdc-' + (cntryCd.length + 1);
385 | } else {
386 | this.separateDialCodeClass = '';
387 | }
388 | }
389 |
390 | /**
391 | * Cleans dialcode from phone number string.
392 | * @param phoneNumber string
393 | */
394 | private removeDialCode(phoneNumber: string): string {
395 | const number = this.getParsedNumber(phoneNumber, this.selectedCountry.iso2);
396 | phoneNumber = this.phoneUtil.format(number, lpn.PhoneNumberFormat[this.numberFormat]);
397 | if (phoneNumber.startsWith('+') && this.separateDialCode) {
398 | phoneNumber = phoneNumber.substr(phoneNumber.indexOf(' ') + 1);
399 | }
400 | return phoneNumber;
401 | }
402 |
403 | /**
404 | * Sifts through all countries and returns iso code of the primary country
405 | * based on the number provided.
406 | * @param countryCode country code in number format
407 | * @param number PhoneNumber object
408 | */
409 | private getCountryIsoCode(countryCode: number, number: lpn.PhoneNumber): string | undefined {
410 | // Will use this to match area code from the first numbers
411 | // @ts-ignore
412 | const rawNumber = number['values_']['2'].toString();
413 | // List of all countries with countryCode (can be more than one. e.x. US, CA, DO, PR all have +1 countryCode)
414 | const countries = this.allCountries.filter(c => c.dialCode === countryCode.toString());
415 | // Main country is the country, which has no areaCodes specified in country-code.ts file.
416 | const mainCountry = countries.find(c => c.areaCodes === undefined);
417 | // Secondary countries are all countries, which have areaCodes specified in country-code.ts file.
418 | const secondaryCountries = countries.filter(c => c.areaCodes !== undefined);
419 | let matchedCountry = mainCountry ? mainCountry.iso2 : undefined;
420 |
421 | /*
422 | Iterate over each secondary country and check if nationalNumber starts with any of areaCodes available.
423 | If no matches found, fallback to the main country.
424 | */
425 | secondaryCountries.forEach(country => {
426 | // @ts-ignore
427 | country.areaCodes.forEach(areaCode => {
428 | if (rawNumber.startsWith(areaCode)) {
429 | matchedCountry = country.iso2;
430 | }
431 | });
432 | });
433 |
434 | return matchedCountry;
435 | }
436 |
437 | /**
438 | * Gets formatted example phone number from phoneUtil.
439 | * @param countryCode string
440 | */
441 | protected getPhoneNumberPlaceHolder(countryCode: string): string {
442 | try {
443 | return this.phoneUtil.format(
444 | this.phoneUtil.getExampleNumber(countryCode),
445 | lpn.PhoneNumberFormat[this.numberFormat]
446 | );
447 | } catch (e) {
448 | // @ts-ignore
449 | return e;
450 | }
451 | }
452 |
453 | /**
454 | * Clearing the list to avoid duplicates (https://github.com/webcat12345/ngx-intl-tel-input/issues/248)
455 | */
456 | protected fetchCountryData(): void {
457 | this.allCountries = [];
458 |
459 | this.countryCodeData.allCountries.forEach(c => {
460 | const country: Country = {
461 | name: c[0].toString(),
462 | iso2: c[1].toString(),
463 | dialCode: c[2].toString(),
464 | priority: +c[3] || 0,
465 | areaCodes: (c[4] as string[]) || undefined,
466 | htmlId: `iti-0__item-${c[1].toString()}`,
467 | flagClass: `iti__${c[1].toString().toLocaleLowerCase()}`,
468 | placeHolder: '',
469 | };
470 |
471 | if (this.enablePlaceholder) {
472 | country.placeHolder = this.getPhoneNumberPlaceHolder(country.iso2.toUpperCase());
473 | }
474 |
475 | this.allCountries.push(country);
476 | });
477 | }
478 |
479 | /**
480 | * Populates preferredCountriesInDropDown with prefferred countries.
481 | */
482 | private updatePreferredCountries() {
483 | if (this.preferredCountries.length) {
484 | this.preferredCountriesInDropDown = [];
485 | this.preferredCountries.forEach(iso2 => {
486 | const preferredCountry = this.allCountries.filter(c => {
487 | return c.iso2 === iso2;
488 | });
489 |
490 | this.preferredCountriesInDropDown.push(preferredCountry[0]);
491 | });
492 | }
493 | }
494 |
495 | /**
496 | * Updates selectedCountry.
497 | */
498 | private updateSelectedCountry() {
499 | if (this.selectedCountryISO) {
500 | // @ts-ignore
501 | this.selectedCountry = this.allCountries.find(c => {
502 | return c.iso2.toLowerCase() === this.selectedCountryISO.toLowerCase();
503 | });
504 | if (this.selectedCountry) {
505 | if (this.phoneNumber) {
506 | this.onPhoneNumberChange();
507 | } else {
508 | // Reason: avoid https://stackoverflow.com/a/54358133/1617590
509 | // tslint:disable-next-line: no-null-keyword
510 | // @ts-ignore
511 | this.propagateChange(null);
512 | }
513 | }
514 | }
515 | }
516 | }
517 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.module.ts:
--------------------------------------------------------------------------------
1 | import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
2 |
3 | import { CommonModule } from '@angular/common';
4 | import { NgModule, ModuleWithProviders } from '@angular/core';
5 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
6 |
7 | import { NativeElementInjectorDirective } from './directives/native-element-injector.directive';
8 | import { NgxIntlTelInputComponent } from './ngx-intl-tel-input.component';
9 |
10 | export const dropdownModuleForRoot: ModuleWithProviders =
11 | BsDropdownModule.forRoot();
12 |
13 | @NgModule({
14 | declarations: [NgxIntlTelInputComponent, NativeElementInjectorDirective],
15 | imports: [CommonModule, FormsModule, ReactiveFormsModule, dropdownModuleForRoot],
16 | exports: [NgxIntlTelInputComponent, NativeElementInjectorDirective],
17 | })
18 | export class NgxIntlTelInputModule {}
19 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.validator.ts:
--------------------------------------------------------------------------------
1 | import * as lpn from 'google-libphonenumber';
2 |
3 | /*
4 | We use "control: any" instead of "control: FormControl" to silence:
5 | "Property 'nativeElement' does not exist on type 'FormControl'".
6 | This happens because I've expanded control with nativeElement via
7 | 'NativeElementInjectorDirective' to get an access to the element.
8 | More about this approach and reasons for this:
9 | https://github.com/angular/angular/issues/18025
10 | https://stackoverflow.com/a/54075119/1617590
11 | */
12 | export const phoneNumberValidator = (control: any) => {
13 | if (!control.value) {
14 | return;
15 | }
16 | // Find inside injected nativeElement and get its "id".
17 | const el: HTMLElement = control.nativeElement as HTMLElement;
18 | const inputBox: HTMLInputElement | any = el ? el.querySelector('input[type="tel"]') : undefined;
19 | if (inputBox) {
20 | const id = inputBox.id;
21 | const isCheckValidation = inputBox.getAttribute('validation');
22 | if (isCheckValidation === 'true') {
23 | const isRequired = control.errors && control.errors.required === true;
24 | const error = { validatePhoneNumber: { valid: false } };
25 |
26 | inputBox.setCustomValidity('Invalid field.');
27 |
28 | let number: lpn.PhoneNumber;
29 |
30 | try {
31 | number = lpn.PhoneNumberUtil.getInstance().parse(
32 | control.value.number,
33 | control.value.countryCode
34 | );
35 | } catch (e) {
36 | if (isRequired) {
37 | return error;
38 | } else {
39 | inputBox.setCustomValidity('');
40 | }
41 | }
42 |
43 | if (control.value) {
44 | // @ts-ignore
45 | if (!number) {
46 | return error;
47 | } else {
48 | if (
49 | !lpn.PhoneNumberUtil.getInstance().isValidNumberForRegion(
50 | number,
51 | control.value.countryCode
52 | )
53 | ) {
54 | return error;
55 | } else {
56 | inputBox.setCustomValidity('');
57 | }
58 | }
59 | }
60 | } else if (isCheckValidation === 'false') {
61 | inputBox.setCustomValidity('');
62 |
63 | control.clearValidators();
64 | }
65 | }
66 | return;
67 | };
68 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/public_api.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Public API Surface of ngx-intl-tel-input
3 | */
4 |
5 | export * from './lib/ngx-intl-tel-input.component';
6 | export * from './lib/directives/native-element-injector.directive';
7 | export * from './lib/ngx-intl-tel-input.module';
8 | export * from './lib/enums/country-iso.enum';
9 | export * from './lib/enums/search-country-field.enum';
10 | export * from './lib/enums/phone-number-format.enum';
11 | export * from './lib/interfaces/change-data';
12 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'core-js/proposals/reflect-metadata';
4 | import 'zone.js';
5 | import 'zone.js/testing';
6 | import { getTestBed } from '@angular/core/testing';
7 | import {
8 | BrowserDynamicTestingModule,
9 | platformBrowserDynamicTesting,
10 | } from '@angular/platform-browser-dynamic/testing';
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
14 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/tsconfig.lib.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "../../tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "../../out-tsc/lib",
6 | "declaration": true,
7 | "declarationMap": true,
8 | "inlineSources": true,
9 | "types": []
10 | },
11 | "exclude": [
12 | "src/test.ts",
13 | "**/*.spec.ts"
14 | ],
15 | "angularCompilerOptions": {
16 | "compilationMode": "partial"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/projects/ngx-intl-tel-input/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../../out-tsc/spec",
5 | "types": [
6 | "jasmine",
7 | "node"
8 | ]
9 | },
10 | "files": [
11 | "src/test.ts"
12 | ],
13 | "include": [
14 | "**/*.spec.ts",
15 | "**/*.d.ts"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/readme-assets/ngx-intl-tel-input.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webcat12345/ngx-intl-tel-input/b23777932b75dff4a035cb27ee59c2fa258567dc/readme-assets/ngx-intl-tel-input.jpg
--------------------------------------------------------------------------------
/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webcat12345/ngx-intl-tel-input/b23777932b75dff4a035cb27ee59c2fa258567dc/src/app/app.component.css
--------------------------------------------------------------------------------
/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Test International Telephone Input Form
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
36 |
37 |
38 |
Is input valid:
39 |
{{ !f.form.controls['phone'].invalid }}
40 |
41 |
42 |
Is input touched:
43 |
{{ f.form.controls['phone'].touched }}
44 |
45 |
46 |
Is form valid:
47 |
{{ f.form.valid }}
48 |
49 |
50 |
Form value:
51 |
{{ f.form.value | json }}
52 |
53 |
54 |
Form validation errors:
55 |
{{ f.form.controls['phone'].errors | json }}
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 |
3 | import { AppComponent } from './app.component';
4 |
5 | describe('AppComponent', () => {
6 | beforeEach(async(() => {
7 | TestBed.configureTestingModule({
8 | declarations: [AppComponent],
9 | }).compileComponents();
10 | }));
11 |
12 | it('should create the app', () => {
13 | const fixture = TestBed.createComponent(AppComponent);
14 | const app = fixture.debugElement.componentInstance;
15 | expect(app).toBeTruthy();
16 | });
17 |
18 | it(`should have as title 'ngx-intl-tel-input-app'`, () => {
19 | const fixture = TestBed.createComponent(AppComponent);
20 | const app = fixture.debugElement.componentInstance;
21 | expect(app.title).toEqual('ngx-intl-tel-input-app');
22 | });
23 |
24 | it('should render title in a h1 tag', () => {
25 | const fixture = TestBed.createComponent(AppComponent);
26 | fixture.detectChanges();
27 | const compiled = fixture.debugElement.nativeElement;
28 | expect(compiled.querySelector('h1').textContent).toContain(
29 | 'Welcome to ngx-intl-tel-input-app!'
30 | );
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { FormControl, FormGroup, Validators } from '@angular/forms';
3 |
4 | import { CountryISO } from 'projects/ngx-intl-tel-input/src/lib/enums/country-iso.enum';
5 | import { PhoneNumberFormat } from 'projects/ngx-intl-tel-input/src/public_api';
6 | import { SearchCountryField } from 'projects/ngx-intl-tel-input/src/lib/enums/search-country-field.enum';
7 |
8 | @Component({
9 | selector: 'app-root',
10 | templateUrl: './app.component.html',
11 | styleUrls: ['./app.component.css'],
12 | })
13 | export class AppComponent {
14 | separateDialCode = false;
15 | SearchCountryField = SearchCountryField;
16 | CountryISO = CountryISO;
17 | PhoneNumberFormat = PhoneNumberFormat;
18 | preferredCountries: CountryISO[] = [CountryISO.UnitedStates, CountryISO.UnitedKingdom];
19 | phoneForm = new FormGroup({
20 | phone: new FormControl(undefined, [Validators.required]),
21 | });
22 |
23 | changePreferredCountries() {
24 | this.preferredCountries = [CountryISO.India, CountryISO.Canada];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { FormsModule, ReactiveFormsModule } from '@angular/forms';
3 | import { BrowserModule } from '@angular/platform-browser';
4 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
5 |
6 | import { NgxIntlTelInputModule } from 'projects/ngx-intl-tel-input/src/lib/ngx-intl-tel-input.module';
7 |
8 | import { AppComponent } from './app.component';
9 |
10 | @NgModule({
11 | declarations: [AppComponent],
12 | imports: [
13 | BrowserModule,
14 | FormsModule,
15 | ReactiveFormsModule,
16 | NgxIntlTelInputModule,
17 | BrowserAnimationsModule,
18 | ],
19 | providers: [],
20 | bootstrap: [AppComponent],
21 | })
22 | export class AppModule {}
23 |
--------------------------------------------------------------------------------
/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webcat12345/ngx-intl-tel-input/b23777932b75dff4a035cb27ee59c2fa258567dc/src/assets/.gitkeep
--------------------------------------------------------------------------------
/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true,
3 | };
4 |
--------------------------------------------------------------------------------
/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false,
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webcat12345/ngx-intl-tel-input/b23777932b75dff4a035cb27ee59c2fa258567dc/src/favicon.ico
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NgxIntlTelInputApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration file, see link for more information
2 | // https://karma-runner.github.io/1.0/config/configuration-file.html
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | basePath: '',
7 | frameworks: ['jasmine', '@angular-devkit/build-angular'],
8 | plugins: [
9 | require('karma-jasmine'),
10 | require('karma-chrome-launcher'),
11 | require('karma-jasmine-html-reporter'),
12 | require('karma-coverage-istanbul-reporter'),
13 | require('@angular-devkit/build-angular/plugins/karma'),
14 | ],
15 | client: {
16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser
17 | },
18 | coverageIstanbulReporter: {
19 | dir: require('path').join(__dirname, '../coverage'),
20 | reports: ['html', 'lcovonly', 'text-summary'],
21 | fixWebpackSourcePaths: true,
22 | },
23 | reporters: ['progress', 'kjhtml'],
24 | port: 9876,
25 | colors: true,
26 | logLevel: config.LOG_INFO,
27 | autoWatch: true,
28 | browsers: ['Chrome'],
29 | singleRun: false,
30 | });
31 | };
32 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic()
12 | .bootstrapModule(AppModule)
13 | .catch(err => console.error(err));
14 |
--------------------------------------------------------------------------------
/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
22 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
23 |
24 | /**
25 | * Web Animations `@angular/platform-browser/animations`
26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
28 | */
29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
30 |
31 | /**
32 | * By default, zone.js will patch all possible macroTask and DomEvents
33 | * user can disable parts of macroTask/DomEvents patch by setting following flags
34 | * because those flags need to be set before `zone.js` being loaded, and webpack
35 | * will put import in the top of bundle, so user need to create a separate file
36 | * in this directory (for example: zone-flags.ts), and put the following flags
37 | * into that file, and then add the following code before importing zone.js.
38 | * import './zone-flags.ts';
39 | *
40 | * The flags allowed in zone-flags.ts are listed here.
41 | *
42 | * The following flags will work for all browsers.
43 | *
44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
47 | *
48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
50 | *
51 | * (window as any).__Zone_enable_cross_context_check = true;
52 | *
53 | */
54 |
55 | /***************************************************************************************************
56 | * Zone JS is required by default for Angular itself.
57 | */
58 | import 'zone.js'; // Included with Angular CLI.
59 |
60 | /***************************************************************************************************
61 | * APPLICATION IMPORTS
62 | */
63 |
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 | body {
3 | font-family: Roboto, Helvetica, Arial, sans-serif;
4 | }
5 |
6 | .wrapper {
7 | margin-bottom: 20px;
8 | }
9 |
10 | /*****************************************************
11 | Custom styling example bellow.
12 | *****************************************************/
13 |
14 | .iti {
15 | display: block !important;
16 | margin-bottom: 20px;
17 | }
18 |
19 | .iti .dropdown-menu.iti__dropdown-content {
20 | border-top-left-radius: 0px;
21 | border-top-right-radius: 0px;
22 | border-color: #c7cace;
23 | margin-top: -1px;
24 | }
25 |
26 | .iti .iti__country-list {
27 | box-shadow: none;
28 | font-size: 14px;
29 | margin-left: 0;
30 | width: 244px;
31 | max-height: 170px;
32 | }
33 |
34 | .iti__flag-container.open + input {
35 | border-bottom-left-radius: 0px;
36 | border-bottom-right-radius: 0px;
37 | }
38 |
39 | .iti .search-container input {
40 | font-size: 14px;
41 | border-color: #c7cace;
42 | border-radius: 0;
43 | padding: 5px 10px;
44 | }
45 |
46 | .iti .search-container input:focus {
47 | outline: none;
48 | }
49 |
50 | @media screen and (max-width: 479px) {
51 | .iti .iti__country-list {
52 | width: 88.3vw;
53 | }
54 | }
55 |
56 | ngx-intl-tel-input input {
57 | height: 44px;
58 | margin-bottom: 20px;
59 | padding: 10px;
60 | border-style: solid;
61 | border-width: 1px;
62 | border-color: #c7cace;
63 | border-radius: 4px;
64 | font-size: 18px;
65 | }
66 |
67 | ngx-intl-tel-input.ng-invalid.ng-touched input {
68 | border: 1px solid #c0392b;
69 | }
70 |
71 | ngx-intl-tel-input input:hover {
72 | box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.24);
73 | }
74 |
75 | ngx-intl-tel-input input:focus {
76 | outline: none !important;
77 | border-color: #3498db;
78 | box-shadow: 0 0 0 0 #000;
79 | }
80 |
81 | ngx-intl-tel-input input::-webkit-input-placeholder {
82 | color: #bac2c7;
83 | }
84 |
85 | ngx-intl-tel-input input:-ms-input-placeholder {
86 | color: #bac2c7;
87 | }
88 |
89 | ngx-intl-tel-input input::-ms-input-placeholder {
90 | color: #bac2c7;
91 | }
92 |
93 | ngx-intl-tel-input input::placeholder {
94 | color: #bac2c7;
95 | }
96 |
97 | ngx-intl-tel-input input[disabled] {
98 | background-color: #e5eaf1;
99 | }
100 |
--------------------------------------------------------------------------------
/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting,
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | // First, initialize the Angular testing environment.
11 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
12 |
--------------------------------------------------------------------------------
/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "files": ["main.ts", "polyfills.ts"],
8 | "include": ["src/**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "types": ["jasmine", "node"]
6 | },
7 | "files": ["test.ts", "polyfills.ts"],
8 | "include": ["**/*.spec.ts", "**/*.d.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
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 | "sourceMap": true,
14 | "paths": {
15 | "my-lib": [
16 | "dist/my-lib/my-lib",
17 | "dist/my-lib"
18 | ]
19 | },
20 | "declaration": false,
21 | "downlevelIteration": true,
22 | "experimentalDecorators": true,
23 | "moduleResolution": "node",
24 | "strictPropertyInitialization": false,
25 | "importHelpers": true,
26 | "target": "es2022",
27 | "module": "es2022",
28 | "lib": [
29 | "es2022",
30 | "dom"
31 | ],
32 | "useDefineForClassFields": false
33 | },
34 | "angularCompilerOptions": {
35 | "enableI18nLegacyMessageIdFormat": false,
36 | "strictInjectionParameters": true,
37 | "strictInputAccessModifiers": true,
38 | "strictTemplates": true
39 | }
40 | }
41 |
--------------------------------------------------------------------------------