├── .babelrc ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── index.html └── index.js ├── package.json ├── src ├── country_data.js ├── flags.png ├── index.js └── react-phone-input-style.less └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015","react"] 3 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.js,*.jsx] 2 | indent_style = tab 3 | indent_size = 2 4 | end_of_line = LF 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*] 10 | charset = utf-8 11 | end_of_line = LF 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Raza Gill 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-phone-input 2 | A simple react component to format a phone number as the user types. 3 | 4 | ![alt tag](http://i.giphy.com/l41m24L5YTSOifyW4.gif) 5 | 6 | ## Installation: 7 | 8 | ```shell-script 9 | npm install react-phone-input --save 10 | ``` 11 | 12 | ## Usage: 13 | 14 | ```jsx 15 | import ReactPhoneInput from 'react-phone-input'; 16 | 17 | React.render( 18 | , 19 | document.getElementById('content')); 20 | ``` 21 | 22 | Your handler for the ``onChange`` event should expect a string as 23 | parameter, where the value is that of the entered phone number. For example: 24 | 25 | ```jsx 26 | function handeOnChange(value) { 27 | this.setState({ 28 | phone: value 29 | }); 30 | } 31 | ``` 32 | ## Options: 33 | 34 | | Name | Description | 35 | | :------------- | :----------- | 36 | | defaultCountry | country code to initialize the component| 37 | | excludeCountries | array of country codes to be excluded e.g. ['cu','cw','kz']| 38 | | onlyCountries | array of country codes to be included e.g. ['cu','cw','kz']| 39 | | preferredCountries | array of country codes to be preferred (highlighted at the top) e.g. ['cu','cw','kz']| 40 | 41 | ## License 42 | 43 | [MIT](https://opensource.org/licenses/MIT) 44 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | react-phone-input-test 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-phone-input", 3 | "version": "1.2.2", 4 | "description": "A react component to format phone numbers", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "TARGET=dev webpack-dev-server --progress", 9 | "build": "TARGET=build webpack -p --progress" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/razagill/react-phone-input.git" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "phone", 18 | "format", 19 | "telephone" 20 | ], 21 | "author": "Raza Gill (http://github.com/razagill)", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/razagill/react-phone-input/issues" 25 | }, 26 | "homepage": "https://github.com/razagill/react-phone-input", 27 | "devDependencies": { 28 | "babel-core": "^6.9.1", 29 | "babel-loader": "^6.2.4", 30 | "babel-preset-es2015": "^6.9.0", 31 | "babel-preset-react": "^6.5.0", 32 | "css-loader": "^0.23.1", 33 | "file-loader": "^0.8.5", 34 | "less": "^2.7.1", 35 | "less-loader": "^2.2.3", 36 | "react-dom": "^15.1.0", 37 | "react-hot-loader": "^1.3.0", 38 | "style-loader": "^0.13.1", 39 | "url-loader": "^0.5.7", 40 | "webpack": "^1.13.1", 41 | "webpack-dev-server": "^1.14.1", 42 | "webpack-merge": "^0.13.0" 43 | }, 44 | "dependencies": { 45 | "classnames": "^2.1.5" 46 | }, 47 | "peerDependencies": { 48 | "lodash": "^4.11.1", 49 | "peerDependencies": { 50 | "lodash": "^4.11.1", 51 | "react": "^15.6.1", 52 | "prop-types": "^15.5.10" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/country_data.js: -------------------------------------------------------------------------------- 1 | // So each country array has the following information: 2 | // [ 3 | // Country name, 4 | // iso2 code, 5 | // International dial code, 6 | // Format (if available), 7 | // Order (if >1 country with same dial code), 8 | // Area codes (if >1 country with same dial code) 9 | // ] 10 | // var _ = require('lodash'); 11 | 12 | var rawAllCountries = [ 13 | [ 14 | "Afghanistan", 15 | "af", 16 | "93" 17 | ], 18 | [ 19 | "Albania", 20 | "al", 21 | "355" 22 | ], 23 | [ 24 | "Algeria ", 25 | "dz", 26 | "213" 27 | ], 28 | [ 29 | "American Samoa", 30 | "as", 31 | "1684" 32 | ], 33 | [ 34 | "Andorra", 35 | "ad", 36 | "376" 37 | ], 38 | [ 39 | "Angola", 40 | "ao", 41 | "244" 42 | ], 43 | [ 44 | "Anguilla", 45 | "ai", 46 | "1264" 47 | ], 48 | [ 49 | "Antigua and Barbuda", 50 | "ag", 51 | "1268" 52 | ], 53 | [ 54 | "Argentina", 55 | "ar", 56 | "54" 57 | ], 58 | [ 59 | "Armenia", 60 | "am", 61 | "374" 62 | ], 63 | [ 64 | "Aruba", 65 | "aw", 66 | "297" 67 | ], 68 | [ 69 | "Australia", 70 | "au", 71 | "61", 72 | "+.. ... ... ..." 73 | ], 74 | [ 75 | "Austria", 76 | "at", 77 | "43" 78 | ], 79 | [ 80 | "Azerbaijan", 81 | "az", 82 | "994" 83 | ], 84 | [ 85 | "Bahamas", 86 | "bs", 87 | "1242" 88 | ], 89 | [ 90 | "Bahrain", 91 | "bh", 92 | "973" 93 | ], 94 | [ 95 | "Bangladesh", 96 | "bd", 97 | "880" 98 | ], 99 | [ 100 | "Barbados", 101 | "bb", 102 | "1246" 103 | ], 104 | [ 105 | "Belarus", 106 | "by", 107 | "375" 108 | ], 109 | [ 110 | "Belgium", 111 | "be", 112 | "32", 113 | "+.. ... .. .. .." 114 | ], 115 | [ 116 | "Belize", 117 | "bz", 118 | "501" 119 | ], 120 | [ 121 | "Benin", 122 | "bj", 123 | "229" 124 | ], 125 | [ 126 | "Bermuda", 127 | "bm", 128 | "1441" 129 | ], 130 | [ 131 | "Bhutan", 132 | "bt", 133 | "975" 134 | ], 135 | [ 136 | "Bolivia", 137 | "bo", 138 | "591" 139 | ], 140 | [ 141 | "Bosnia and Herzegovina", 142 | "ba", 143 | "387" 144 | ], 145 | [ 146 | "Botswana", 147 | "bw", 148 | "267" 149 | ], 150 | [ 151 | "Brazil", 152 | "br", 153 | "55" 154 | ], 155 | [ 156 | "British Indian Ocean Territory", 157 | "io", 158 | "246" 159 | ], 160 | [ 161 | "British Virgin Islands", 162 | "vg", 163 | "1284" 164 | ], 165 | [ 166 | "Brunei", 167 | "bn", 168 | "673" 169 | ], 170 | [ 171 | "Bulgaria", 172 | "bg", 173 | "359" 174 | ], 175 | [ 176 | "Burkina Faso", 177 | "bf", 178 | "226" 179 | ], 180 | [ 181 | "Burundi", 182 | "bi", 183 | "257" 184 | ], 185 | [ 186 | "Cambodia", 187 | "kh", 188 | "855" 189 | ], 190 | [ 191 | "Cameroon", 192 | "cm", 193 | "237" 194 | ], 195 | [ 196 | "Canada", 197 | "ca", 198 | "1", 199 | "+. (...) ...-....", 200 | 1, 201 | ["204", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"] 202 | ], 203 | [ 204 | "Cape Verde", 205 | "cv", 206 | "238" 207 | ], 208 | [ 209 | "Caribbean Netherlands", 210 | "bq", 211 | "599", 212 | "", 213 | 1 214 | ], 215 | [ 216 | "Cayman Islands", 217 | "ky", 218 | "1345" 219 | ], 220 | [ 221 | "Central African Republic", 222 | "cf", 223 | "236" 224 | ], 225 | [ 226 | "Chad", 227 | "td", 228 | "235" 229 | ], 230 | [ 231 | "Chile", 232 | "cl", 233 | "56" 234 | ], 235 | [ 236 | "China", 237 | "cn", 238 | "86", 239 | "+.. ..-........" 240 | ], 241 | [ 242 | "Colombia", 243 | "co", 244 | "57" 245 | ], 246 | [ 247 | "Comoros", 248 | "km", 249 | "269" 250 | ], 251 | [ 252 | "Congo", 253 | "cd", 254 | "243" 255 | ], 256 | [ 257 | "Congo", 258 | "cg", 259 | "242" 260 | ], 261 | [ 262 | "Cook Islands", 263 | "ck", 264 | "682" 265 | ], 266 | [ 267 | "Costa Rica", 268 | "cr", 269 | "506", 270 | "+... ....-...." 271 | ], 272 | [ 273 | "Côte d’Ivoire", 274 | "ci", 275 | "225" 276 | ], 277 | [ 278 | "Croatia", 279 | "hr", 280 | "385" 281 | ], 282 | [ 283 | "Cuba", 284 | "cu", 285 | "53" 286 | ], 287 | [ 288 | "Curaçao", 289 | "cw", 290 | "599", 291 | "", 292 | 0 293 | ], 294 | [ 295 | "Cyprus", 296 | "cy", 297 | "357" 298 | ], 299 | [ 300 | "Czech Republic", 301 | "cz", 302 | "420" 303 | ], 304 | [ 305 | "Denmark", 306 | "dk", 307 | "45", 308 | "+.. .. .. .. .." 309 | ], 310 | [ 311 | "Djibouti", 312 | "dj", 313 | "253" 314 | ], 315 | [ 316 | "Dominica", 317 | "dm", 318 | "1767" 319 | ], 320 | [ 321 | "Dominican Republic", 322 | "do", 323 | "1", 324 | "", 325 | 2, 326 | ["809", "829", "849"] 327 | ], 328 | [ 329 | "Ecuador", 330 | "ec", 331 | "593" 332 | ], 333 | [ 334 | "Egypt", 335 | "eg", 336 | "20" 337 | ], 338 | [ 339 | "El Salvador", 340 | "sv", 341 | "503", 342 | "+... ....-...." 343 | ], 344 | [ 345 | "Equatorial Guinea", 346 | "gq", 347 | "240" 348 | ], 349 | [ 350 | "Eritrea", 351 | "er", 352 | "291" 353 | ], 354 | [ 355 | "Estonia", 356 | "ee", 357 | "372" 358 | ], 359 | [ 360 | "Ethiopia", 361 | "et", 362 | "251" 363 | ], 364 | [ 365 | "Falkland Islands", 366 | "fk", 367 | "500" 368 | ], 369 | [ 370 | "Faroe Islands", 371 | "fo", 372 | "298" 373 | ], 374 | [ 375 | "Fiji", 376 | "fj", 377 | "679" 378 | ], 379 | [ 380 | "Finland", 381 | "fi", 382 | "358", 383 | "+... .. ... .. .." 384 | ], 385 | [ 386 | "France", 387 | "fr", 388 | "33", 389 | "+.. . .. .. .. .." 390 | ], 391 | [ 392 | "French Guiana", 393 | "gf", 394 | "594" 395 | ], 396 | [ 397 | "French Polynesia", 398 | "pf", 399 | "689" 400 | ], 401 | [ 402 | "Gabon", 403 | "ga", 404 | "241" 405 | ], 406 | [ 407 | "Gambia", 408 | "gm", 409 | "220" 410 | ], 411 | [ 412 | "Georgia", 413 | "ge", 414 | "995" 415 | ], 416 | [ 417 | "Germany", 418 | "de", 419 | "49", 420 | "+.. ... ......." 421 | ], 422 | [ 423 | "Ghana", 424 | "gh", 425 | "233" 426 | ], 427 | [ 428 | "Gibraltar", 429 | "gi", 430 | "350" 431 | ], 432 | [ 433 | "Greece", 434 | "gr", 435 | "30" 436 | ], 437 | [ 438 | "Greenland", 439 | "gl", 440 | "299" 441 | ], 442 | [ 443 | "Grenada", 444 | "gd", 445 | "1473" 446 | ], 447 | [ 448 | "Guadeloupe", 449 | "gp", 450 | "590", 451 | "", 452 | 0 453 | ], 454 | [ 455 | "Guam", 456 | "gu", 457 | "1671" 458 | ], 459 | [ 460 | "Guatemala", 461 | "gt", 462 | "502", 463 | "+... ....-...." 464 | ], 465 | [ 466 | "Guinea", 467 | "gn", 468 | "224" 469 | ], 470 | [ 471 | "Guinea-Bissau", 472 | "gw", 473 | "245" 474 | ], 475 | [ 476 | "Guyana", 477 | "gy", 478 | "592" 479 | ], 480 | [ 481 | "Haiti", 482 | "ht", 483 | "509", 484 | "+... ....-...." 485 | ], 486 | [ 487 | "Honduras", 488 | "hn", 489 | "504" 490 | ], 491 | [ 492 | "Hong Kong", 493 | "hk", 494 | "852", 495 | "+... .... ...." 496 | ], 497 | [ 498 | "Hungary", 499 | "hu", 500 | "36" 501 | ], 502 | [ 503 | "Iceland", 504 | "is", 505 | "354", 506 | "+... ... ...." 507 | ], 508 | [ 509 | "India", 510 | "in", 511 | "91", 512 | "+.. .....-....." 513 | ], 514 | [ 515 | "Indonesia", 516 | "id", 517 | "62" 518 | ], 519 | [ 520 | "Iran", 521 | "ir", 522 | "98" 523 | ], 524 | [ 525 | "Iraq", 526 | "iq", 527 | "964" 528 | ], 529 | [ 530 | "Ireland", 531 | "ie", 532 | "353", 533 | "+... .. ......." 534 | ], 535 | [ 536 | "Israel", 537 | "il", 538 | "972" 539 | ], 540 | [ 541 | "Italy", 542 | "it", 543 | "39", 544 | "+.. ... ......", 545 | 0 546 | ], 547 | [ 548 | "Jamaica", 549 | "jm", 550 | "1876" 551 | ], 552 | [ 553 | "Japan", 554 | "jp", 555 | "81", 556 | "+.. ... .. ...." 557 | ], 558 | [ 559 | "Jordan", 560 | "jo", 561 | "962" 562 | ], 563 | [ 564 | "Kazakhstan", 565 | "kz", 566 | "7", 567 | "+. ... ...-..-..", 568 | 1 569 | ], 570 | [ 571 | "Kenya", 572 | "ke", 573 | "254" 574 | ], 575 | [ 576 | "Kiribati", 577 | "ki", 578 | "686" 579 | ], 580 | [ 581 | "Kuwait", 582 | "kw", 583 | "965" 584 | ], 585 | [ 586 | "Kyrgyzstan", 587 | "kg", 588 | "996" 589 | ], 590 | [ 591 | "Laos", 592 | "la", 593 | "856" 594 | ], 595 | [ 596 | "Latvia", 597 | "lv", 598 | "371" 599 | ], 600 | [ 601 | "Lebanon", 602 | "lb", 603 | "961" 604 | ], 605 | [ 606 | "Lesotho", 607 | "ls", 608 | "266" 609 | ], 610 | [ 611 | "Liberia", 612 | "lr", 613 | "231" 614 | ], 615 | [ 616 | "Libya", 617 | "ly", 618 | "218" 619 | ], 620 | [ 621 | "Liechtenstein", 622 | "li", 623 | "423" 624 | ], 625 | [ 626 | "Lithuania", 627 | "lt", 628 | "370" 629 | ], 630 | [ 631 | "Luxembourg", 632 | "lu", 633 | "352" 634 | ], 635 | [ 636 | "Macau", 637 | "mo", 638 | "853" 639 | ], 640 | [ 641 | "Macedonia", 642 | "mk", 643 | "389" 644 | ], 645 | [ 646 | "Madagascar", 647 | "mg", 648 | "261" 649 | ], 650 | [ 651 | "Malawi", 652 | "mw", 653 | "265" 654 | ], 655 | [ 656 | "Malaysia", 657 | "my", 658 | "60", 659 | "+.. ..-....-...." 660 | ], 661 | [ 662 | "Maldives", 663 | "mv", 664 | "960" 665 | ], 666 | [ 667 | "Mali", 668 | "ml", 669 | "223" 670 | ], 671 | [ 672 | "Malta", 673 | "mt", 674 | "356" 675 | ], 676 | [ 677 | "Marshall Islands", 678 | "mh", 679 | "692" 680 | ], 681 | [ 682 | "Martinique", 683 | "mq", 684 | "596" 685 | ], 686 | [ 687 | "Mauritania", 688 | "mr", 689 | "222" 690 | ], 691 | [ 692 | "Mauritius", 693 | "mu", 694 | "230" 695 | ], 696 | [ 697 | "Mexico", 698 | "mx", 699 | "52" 700 | ], 701 | [ 702 | "Micronesia", 703 | "fm", 704 | "691" 705 | ], 706 | [ 707 | "Moldova", 708 | "md", 709 | "373" 710 | ], 711 | [ 712 | "Monaco", 713 | "mc", 714 | "377" 715 | ], 716 | [ 717 | "Mongolia", 718 | "mn", 719 | "976" 720 | ], 721 | [ 722 | "Montenegro", 723 | "me", 724 | "382" 725 | ], 726 | [ 727 | "Montserrat", 728 | "ms", 729 | "1664" 730 | ], 731 | [ 732 | "Morocco", 733 | "ma", 734 | "212" 735 | ], 736 | [ 737 | "Mozambique", 738 | "mz", 739 | "258" 740 | ], 741 | [ 742 | "Myanmar", 743 | "mm", 744 | "95" 745 | ], 746 | [ 747 | "Namibia", 748 | "na", 749 | "264" 750 | ], 751 | [ 752 | "Nauru", 753 | "nr", 754 | "674" 755 | ], 756 | [ 757 | "Nepal", 758 | "np", 759 | "977" 760 | ], 761 | [ 762 | "Netherlands", 763 | "nl", 764 | "31", 765 | "+.. .. ........" 766 | ], 767 | [ 768 | "New Caledonia", 769 | "nc", 770 | "687" 771 | ], 772 | [ 773 | "New Zealand", 774 | "nz", 775 | "64", 776 | "+.. ...-...-...." 777 | ], 778 | [ 779 | "Nicaragua", 780 | "ni", 781 | "505" 782 | ], 783 | [ 784 | "Niger", 785 | "ne", 786 | "227" 787 | ], 788 | [ 789 | "Nigeria", 790 | "ng", 791 | "234" 792 | ], 793 | [ 794 | "Niue", 795 | "nu", 796 | "683" 797 | ], 798 | [ 799 | "Norfolk Island", 800 | "nf", 801 | "672" 802 | ], 803 | [ 804 | "North Korea", 805 | "kp", 806 | "850" 807 | ], 808 | [ 809 | "Northern Mariana Islands", 810 | "mp", 811 | "1670" 812 | ], 813 | [ 814 | "Norway", 815 | "no", 816 | "47", 817 | "+.. ... .. ..." 818 | ], 819 | [ 820 | "Oman", 821 | "om", 822 | "968" 823 | ], 824 | [ 825 | "Pakistan", 826 | "pk", 827 | "92", 828 | "+.. ...-......." 829 | ], 830 | [ 831 | "Palau", 832 | "pw", 833 | "680" 834 | ], 835 | [ 836 | "Palestine", 837 | "ps", 838 | "970" 839 | ], 840 | [ 841 | "Panama", 842 | "pa", 843 | "507" 844 | ], 845 | [ 846 | "Papua New Guinea", 847 | "pg", 848 | "675" 849 | ], 850 | [ 851 | "Paraguay", 852 | "py", 853 | "595" 854 | ], 855 | [ 856 | "Peru", 857 | "pe", 858 | "51" 859 | ], 860 | [ 861 | "Philippines", 862 | "ph", 863 | "63", 864 | "+.. ... ...." 865 | ], 866 | [ 867 | "Poland", 868 | "pl", 869 | "48", 870 | "+.. ...-...-..." 871 | ], 872 | [ 873 | "Portugal", 874 | "pt", 875 | "351" 876 | ], 877 | [ 878 | "Puerto Rico", 879 | "pr", 880 | "1", 881 | "", 882 | 3, 883 | ["787", "939"] 884 | ], 885 | [ 886 | "Qatar", 887 | "qa", 888 | "974" 889 | ], 890 | [ 891 | "Réunion", 892 | "re", 893 | "262" 894 | ], 895 | [ 896 | "Romania", 897 | "ro", 898 | "40" 899 | ], 900 | [ 901 | "Russia", 902 | "ru", 903 | "7", 904 | "+. ... ...-..-..", 905 | 0 906 | ], 907 | [ 908 | "Rwanda", 909 | "rw", 910 | "250" 911 | ], 912 | [ 913 | "Saint Barthélemy", 914 | "bl", 915 | "590", 916 | "", 917 | 1 918 | ], 919 | [ 920 | "Saint Helena", 921 | "sh", 922 | "290" 923 | ], 924 | [ 925 | "Saint Kitts and Nevis", 926 | "kn", 927 | "1869" 928 | ], 929 | [ 930 | "Saint Lucia", 931 | "lc", 932 | "1758" 933 | ], 934 | [ 935 | "Saint Martin", 936 | "mf", 937 | "590", 938 | "", 939 | 2 940 | ], 941 | [ 942 | "Saint Pierre and Miquelon", 943 | "pm", 944 | "508" 945 | ], 946 | [ 947 | "Saint Vincent and the Grenadines", 948 | "vc", 949 | "1784" 950 | ], 951 | [ 952 | "Samoa", 953 | "ws", 954 | "685" 955 | ], 956 | [ 957 | "San Marino", 958 | "sm", 959 | "378" 960 | ], 961 | [ 962 | "São Tomé and Príncipe", 963 | "st", 964 | "239" 965 | ], 966 | [ 967 | "Saudi Arabia", 968 | "sa", 969 | "966" 970 | ], 971 | [ 972 | "Senegal", 973 | "sn", 974 | "221" 975 | ], 976 | [ 977 | "Serbia", 978 | "rs", 979 | "381" 980 | ], 981 | [ 982 | "Seychelles", 983 | "sc", 984 | "248" 985 | ], 986 | [ 987 | "Sierra Leone", 988 | "sl", 989 | "232" 990 | ], 991 | [ 992 | "Singapore", 993 | "sg", 994 | "65", 995 | "+.. ....-...." 996 | ], 997 | [ 998 | "Sint Maarten", 999 | "sx", 1000 | "1721" 1001 | ], 1002 | [ 1003 | "Slovakia", 1004 | "sk", 1005 | "421" 1006 | ], 1007 | [ 1008 | "Slovenia", 1009 | "si", 1010 | "386" 1011 | ], 1012 | [ 1013 | "Solomon Islands", 1014 | "sb", 1015 | "677" 1016 | ], 1017 | [ 1018 | "Somalia", 1019 | "so", 1020 | "252" 1021 | ], 1022 | [ 1023 | "South Africa", 1024 | "za", 1025 | "27" 1026 | ], 1027 | [ 1028 | "South Korea", 1029 | "kr", 1030 | "82" 1031 | ], 1032 | [ 1033 | "South Sudan", 1034 | "ss", 1035 | "211" 1036 | ], 1037 | [ 1038 | "Spain", 1039 | "es", 1040 | "34", 1041 | "+.. ... ... ..." 1042 | ], 1043 | [ 1044 | "Sri Lanka", 1045 | "lk", 1046 | "94" 1047 | ], 1048 | [ 1049 | "Sudan", 1050 | "sd", 1051 | "249" 1052 | ], 1053 | [ 1054 | "Suriname", 1055 | "sr", 1056 | "597" 1057 | ], 1058 | [ 1059 | "Swaziland", 1060 | "sz", 1061 | "268" 1062 | ], 1063 | [ 1064 | "Sweden", 1065 | "se", 1066 | "46", 1067 | "+.. .. ... .. .." 1068 | ], 1069 | [ 1070 | "Switzerland", 1071 | "ch", 1072 | "41", 1073 | "+.. .. ... .. .." 1074 | ], 1075 | [ 1076 | "Syria", 1077 | "sy", 1078 | "963" 1079 | ], 1080 | [ 1081 | "Taiwan", 1082 | "tw", 1083 | "886" 1084 | ], 1085 | [ 1086 | "Tajikistan", 1087 | "tj", 1088 | "992" 1089 | ], 1090 | [ 1091 | "Tanzania", 1092 | "tz", 1093 | "255" 1094 | ], 1095 | [ 1096 | "Thailand", 1097 | "th", 1098 | "66" 1099 | ], 1100 | [ 1101 | "Timor-Leste", 1102 | "tl", 1103 | "670" 1104 | ], 1105 | [ 1106 | "Togo", 1107 | "tg", 1108 | "228" 1109 | ], 1110 | [ 1111 | "Tokelau", 1112 | "tk", 1113 | "690" 1114 | ], 1115 | [ 1116 | "Tonga", 1117 | "to", 1118 | "676" 1119 | ], 1120 | [ 1121 | "Trinidad and Tobago", 1122 | "tt", 1123 | "1868" 1124 | ], 1125 | [ 1126 | "Tunisia", 1127 | "tn", 1128 | "216" 1129 | ], 1130 | [ 1131 | "Turkey", 1132 | "tr", 1133 | "90", 1134 | "+.. ... ... .. .." 1135 | ], 1136 | [ 1137 | "Turkmenistan", 1138 | "tm", 1139 | "993" 1140 | ], 1141 | [ 1142 | "Turks and Caicos Islands", 1143 | "tc", 1144 | "1649" 1145 | ], 1146 | [ 1147 | "Tuvalu", 1148 | "tv", 1149 | "688" 1150 | ], 1151 | [ 1152 | "U.S. Virgin Islands", 1153 | "vi", 1154 | "1340" 1155 | ], 1156 | [ 1157 | "Uganda", 1158 | "ug", 1159 | "256" 1160 | ], 1161 | [ 1162 | "Ukraine", 1163 | "ua", 1164 | "380" 1165 | ], 1166 | [ 1167 | "United Arab Emirates", 1168 | "ae", 1169 | "971" 1170 | ], 1171 | [ 1172 | "United Kingdom", 1173 | "gb", 1174 | "44", 1175 | "+.. .... ......" 1176 | ], 1177 | [ 1178 | "United States", 1179 | "us", 1180 | "1", 1181 | "+. (...) ...-....", 1182 | 0, 1183 | ["907", "205", "251", "256", "334", "479", "501", "870", "480", "520", "602", "623", "928", "209", "213", "310", "323", "408", "415", "510", "530", "559", "562", "619", "626", "650", "661", "707", "714", "760", "805", "818", "831", "858", "909", "916", "925", "949", "951", "303", "719", "970", "203", "860", "202", "302", "239", "305", "321", "352", "386", "407", "561", "727", "772", "813", "850", "863", "904", "941", "954", "229", "404", "478", "706", "770", "912", "808", "319", "515", "563", "641", "712", "208", "217", "309", "312", "618", "630", "708", "773", "815", "847", "219", "260", "317", "574", "765", "812", "316", "620", "785", "913", "270", "502", "606", "859", "225", "318", "337", "504", "985", "413", "508", "617", "781", "978", "301", "410", "207", "231", "248", "269", "313", "517", "586", "616", "734", "810", "906", "989", "218", "320", "507", "612", "651", "763", "952", "314", "417", "573", "636", "660", "816", "228", "601", "662", "406", "252", "336", "704", "828", "910", "919", "701", "308", "402", "603", "201", "609", "732", "856", "908", "973", "505", "575", "702", "775", "212", "315", "516", "518", "585", "607", "631", "716", "718", "845", "914", "216", "330", "419", "440", "513", "614", "740", "937", "405", "580", "918", "503", "541", "215", "412", "570", "610", "717", "724", "814", "401", "803", "843", "864", "605", "423", "615", "731", "865", "901", "931", "210", "214", "254", "281", "325", "361", "409", "432", "512", "713", "806", "817", "830", "903", "915", "936", "940", "956", "972", "979", "435", "801", "276", "434", "540", "703", "757", "804", "802", "206", "253", "360", "425", "509", "262", "414", "608", "715", "920", "304", "307"] 1184 | ], 1185 | [ 1186 | "Uruguay", 1187 | "uy", 1188 | "598" 1189 | ], 1190 | [ 1191 | "Uzbekistan", 1192 | "uz", 1193 | "998" 1194 | ], 1195 | [ 1196 | "Vanuatu", 1197 | "vu", 1198 | "678" 1199 | ], 1200 | [ 1201 | "Vatican City", 1202 | "va", 1203 | "39", 1204 | "+.. .. .... ....", 1205 | 1 1206 | ], 1207 | [ 1208 | "Venezuela", 1209 | "ve", 1210 | "58" 1211 | ], 1212 | [ 1213 | "Vietnam", 1214 | "vn", 1215 | "84" 1216 | ], 1217 | [ 1218 | "Wallis and Futuna", 1219 | "wf", 1220 | "681" 1221 | ], 1222 | [ 1223 | "Yemen", 1224 | "ye", 1225 | "967" 1226 | ], 1227 | [ 1228 | "Zambia", 1229 | "zm", 1230 | "260" 1231 | ], 1232 | [ 1233 | "Zimbabwe", 1234 | "zw", 1235 | "263" 1236 | ] 1237 | ]; 1238 | 1239 | // we will build this in the loop below 1240 | var allCountries = []; 1241 | var allCountryCodes = {}; 1242 | function addCountryCode (iso2, dialCode, priority) { 1243 | if (!(dialCode in allCountryCodes)) { 1244 | allCountryCodes[dialCode] = []; 1245 | } 1246 | var index = priority || 0; 1247 | allCountryCodes[dialCode][index] = iso2; 1248 | }; 1249 | 1250 | // loop over all of the countries above 1251 | // allCountries2 = _.map(allCountries, function(country) { 1252 | // return { 1253 | // name: country[0], 1254 | // iso2: country[1], 1255 | // dialCode: country[2], 1256 | // format: country[3], 1257 | // hasAreaCodes: country.length > 4 1258 | // } 1259 | // }); 1260 | 1261 | for (let i = 0; i < rawAllCountries.length; i++) { 1262 | // countries 1263 | 1264 | let c = rawAllCountries[i]; 1265 | allCountries.push({ 1266 | name: c[0], 1267 | iso2: c[1], 1268 | dialCode: c[2], 1269 | priority: c[4] || 0 1270 | }); 1271 | let countryIdx = allCountries.length - 1; 1272 | 1273 | // format 1274 | if (c[3]) { 1275 | allCountries[countryIdx].format = c[3]; 1276 | } 1277 | 1278 | // area codes 1279 | if (c[5]) { 1280 | allCountries[countryIdx].hasAreaCodes = true; 1281 | for (var j = 0; j < c[5].length; j++) { 1282 | // full dial code is country code + dial code 1283 | var dialCode = c[2] + c[5][j]; 1284 | addCountryCode(c[1], dialCode); 1285 | 1286 | let virtualCountry = Object.assign({},allCountries[countryIdx]); 1287 | virtualCountry.dialCode = dialCode; 1288 | allCountries.push(virtualCountry); 1289 | } 1290 | } 1291 | 1292 | // dial codes 1293 | addCountryCode( 1294 | allCountries[countryIdx].iso2, 1295 | allCountries[countryIdx].dialCode, 1296 | allCountries[countryIdx].hasAreaCodes 1297 | ); 1298 | 1299 | } 1300 | 1301 | module.exports = { 1302 | allCountries: allCountries, 1303 | allCountryCodes: allCountryCodes 1304 | }; 1305 | -------------------------------------------------------------------------------- /src/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sample-usr/react-phone-input/a8875b225dfde4a539e5d5ca9dc44ace57f2d8f9/src/flags.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // TODO - fix the onlyContries props. Currently expects that as an array of country object, but users should be able to send in array of country isos 2 | 3 | import { some, find, reduce, map, filter, includes } from 'lodash/collection'; 4 | import { findIndex, head, tail } from 'lodash/array'; 5 | import { debounce, memoize } from 'lodash/function'; 6 | import { trim, startsWith } from 'lodash/string'; 7 | import React from 'react'; 8 | import PropTypes from 'prop-types'; 9 | import countryData from './country_data.js'; 10 | import classNames from 'classnames'; 11 | import ReactDOM from 'react-dom'; 12 | 13 | let allCountries = countryData.allCountries; 14 | 15 | let isModernBrowser = Boolean(document.createElement('input').setSelectionRange); 16 | 17 | var style = require('./react-phone-input-style.less'); 18 | 19 | let keys = { 20 | UP: 38, 21 | DOWN: 40, 22 | RIGHT: 39, 23 | LEFT: 37, 24 | ENTER: 13, 25 | ESC: 27, 26 | PLUS: 43, 27 | A: 65, 28 | Z: 90, 29 | SPACE: 32 30 | }; 31 | 32 | function isNumberValid(inputNumber) { 33 | let countries = countryData.allCountries; 34 | return some(countries, function(country) { 35 | return startsWith(inputNumber, country.dialCode) || startsWith(country.dialCode, inputNumber); 36 | }); 37 | } 38 | 39 | function getOnlyCountries(onlyCountriesArray) { 40 | if (onlyCountriesArray.length === 0) { 41 | return allCountries; 42 | } else { 43 | let selectedCountries = []; 44 | allCountries.map(function(country) { 45 | onlyCountriesArray.map(function(selCountry){ 46 | if (country.iso2 === selCountry) { 47 | selectedCountries.push(country); 48 | } 49 | }); 50 | }); 51 | return selectedCountries; 52 | } 53 | } 54 | 55 | function excludeCountries(selectedCountries, excludedCountries) { 56 | if(excludedCountries.length === 0) { 57 | return selectedCountries; 58 | } else { 59 | return filter(selectedCountries, function(selCountry) { 60 | return !includes(excludedCountries, selCountry.iso2); 61 | }); 62 | } 63 | } 64 | 65 | class ReactPhoneInput extends React.Component { 66 | 67 | constructor(props) { 68 | super(props); 69 | let inputNumber = this.props.value || ''; 70 | let onlyCountries = excludeCountries(getOnlyCountries(props.onlyCountries), props.excludeCountries); 71 | let selectedCountryGuess = find(onlyCountries, {iso2: this.props.defaultCountry}); 72 | let selectedCountryGuessIndex = findIndex(allCountries, selectedCountryGuess); 73 | let dialCode = selectedCountryGuess && !startsWith(inputNumber.replace(/\D/g, ''), selectedCountryGuess.dialCode) ? selectedCountryGuess.dialCode : ''; 74 | let formattedNumber = this.formatNumber(dialCode + inputNumber.replace(/\D/g, ''), selectedCountryGuess ? selectedCountryGuess.format : null); 75 | let preferredCountries = filter(allCountries, (country) => { 76 | return some(this.props.preferredCountries, (preferredCountry) => { 77 | return preferredCountry === country.iso2; 78 | }); 79 | }); 80 | this.getNumber = this.getNumber.bind(this); 81 | this.getValue = this.getValue.bind(this); 82 | this.resetNumber = this.resetNumber.bind(this); 83 | this.scrollTo = this.scrollTo.bind(this); 84 | this.formatNumber = this.formatNumber.bind(this); 85 | this._cursorToEnd = this._cursorToEnd.bind(this); 86 | this.guessSelectedCountry = this.guessSelectedCountry.bind(this); 87 | this.getElement = this.getElement.bind(this); 88 | this.handleFlagDropdownClick = this.handleFlagDropdownClick.bind(this); 89 | this.handleInput = this.handleInput.bind(this); 90 | this.handleInputBlur = this.handleInputBlur.bind(this); 91 | this.handleInputClick = this.handleInputClick.bind(this); 92 | this.handleFlagItemClick = this.handleFlagItemClick.bind(this); 93 | this.handleInputFocus = this.handleInputFocus.bind(this); 94 | this._getHighlightCountryIndex = this._getHighlightCountryIndex.bind(this); 95 | this._searchCountry = this._searchCountry.bind(this); 96 | this.searchCountry = this.searchCountry.bind(this); 97 | this.handleKeydown = this.handleKeydown.bind(this); 98 | this.handleInputKeyDown = this.handleInputKeyDown.bind(this); 99 | this.getCountryDropDownList = this.getCountryDropDownList.bind(this); 100 | 101 | this.state = { 102 | defaultCountry: props.defaultCountry, 103 | preferredCountries: preferredCountries, 104 | selectedCountry: selectedCountryGuess, 105 | highlightCountryIndex: selectedCountryGuessIndex, 106 | formattedNumber: formattedNumber, 107 | showDropDown: false, 108 | queryString: '', 109 | freezeSelection: false, 110 | debouncedQueryStingSearcher: debounce(this.searchCountry, 100), 111 | onlyCountries: onlyCountries 112 | }; 113 | } 114 | 115 | getNumber() { 116 | return this.state.formattedNumber !== '+' ? this.state.formattedNumber : ''; 117 | } 118 | 119 | getValue() { 120 | return this.getNumber(); 121 | } 122 | 123 | resetNumber() { 124 | let formattedNumber = this.formatNumber(this.state.selectedCountry.dialCode, this.state.selectedCountry.format); 125 | this.setState({ 126 | formattedNumber: formattedNumber 127 | }) 128 | } 129 | 130 | updateDefaultCountry(country) { 131 | const newSelectedCountry = find(this.state.onlyCountries, {iso2: country}); 132 | this.setState({ 133 | defaultCountry: country, 134 | selectedCountry: newSelectedCountry, 135 | formattedNumber: '+' + newSelectedCountry.dialCode 136 | }); 137 | } 138 | 139 | componentDidMount() { 140 | document.addEventListener('keydown', this.handleKeydown); 141 | } 142 | 143 | componentWillUnmount() { 144 | document.removeEventListener('keydown', this.handleKeydown); 145 | } 146 | 147 | componentWillReceiveProps(nextProps) { 148 | if (nextProps.defaultCountry && 149 | nextProps.defaultCountry !== this.state.defaultCountry) { 150 | this.updateDefaultCountry(nextProps.defaultCountry); 151 | } 152 | } 153 | 154 | scrollTo(country, middle) { 155 | if(!country) return; 156 | 157 | let container = ReactDOM.findDOMNode(this.refs.flagDropdownList); 158 | 159 | if(!container) return; 160 | 161 | let containerHeight = container.offsetHeight; 162 | let containerOffset = container.getBoundingClientRect(); 163 | let containerTop = containerOffset.top + document.body.scrollTop; 164 | let containerBottom = containerTop + containerHeight; 165 | 166 | let element = country; 167 | let elementOffset = element.getBoundingClientRect(); 168 | 169 | let elementHeight = element.offsetHeight; 170 | let elementTop = elementOffset.top + document.body.scrollTop; 171 | let elementBottom = elementTop + elementHeight; 172 | let newScrollTop = elementTop - containerTop + container.scrollTop; 173 | let middleOffset = (containerHeight / 2) - (elementHeight / 2); 174 | 175 | if (elementTop < containerTop) { 176 | // scroll up 177 | if (middle) { 178 | newScrollTop -= middleOffset; 179 | } 180 | container.scrollTop = newScrollTop; 181 | } else if (elementBottom > containerBottom) { 182 | // scroll down 183 | if(middle) { 184 | newScrollTop += middleOffset; 185 | } 186 | var heightDifference = containerHeight - elementHeight; 187 | container.scrollTop = newScrollTop - heightDifference; 188 | } 189 | } 190 | 191 | formatNumber(text, pattern) { 192 | if(!text || text.length === 0) { 193 | return '+'; 194 | } 195 | 196 | // for all strings with length less than 3, just return it (1, 2 etc.) 197 | // also return the same text if the selected country has no fixed format 198 | if((text && text.length < 2) || !pattern || !this.props.autoFormat) { 199 | return `+${text}`; 200 | } 201 | 202 | let formattedObject = reduce(pattern, function(acc, character) { 203 | if(acc.remainingText.length === 0) { 204 | return acc; 205 | } 206 | 207 | if(character !== '.') { 208 | return { 209 | formattedText: acc.formattedText + character, 210 | remainingText: acc.remainingText 211 | }; 212 | } 213 | 214 | return { 215 | formattedText: acc.formattedText + head(acc.remainingText), 216 | remainingText: tail(acc.remainingText) 217 | }; 218 | }, {formattedText: '', remainingText: text.split('')}); 219 | return formattedObject.formattedText + formattedObject.remainingText.join(''); 220 | } 221 | 222 | // put the cursor to the end of the input (usually after a focus event) 223 | _cursorToEnd() { 224 | let input = ReactDOM.findDOMNode(this.refs.numberInput); 225 | input.focus(); 226 | if (isModernBrowser) { 227 | let len = input.value.length; 228 | input.setSelectionRange(len, len); 229 | } 230 | } 231 | 232 | getElement(index) { 233 | return ReactDOM.findDOMNode(this.refs[`flag_no_${index}`]); 234 | } 235 | 236 | handleFlagDropdownClick() { 237 | // need to put the highlight on the current selected country if the dropdown is going to open up 238 | this.setState({ 239 | showDropDown: !this.state.showDropDown, 240 | highlightCountry: find(this.state.onlyCountries, this.state.selectedCountry), 241 | highlightCountryIndex: findIndex(this.state.onlyCountries, this.state.selectedCountry) 242 | }, () => { 243 | if(this.state.showDropDown) { 244 | this.scrollTo(this.getElement(this.state.highlightCountryIndex + this.state.preferredCountries.length)); 245 | } 246 | }); 247 | } 248 | 249 | handleInput(event) { 250 | 251 | let formattedNumber = '+', newSelectedCountry = this.state.selectedCountry, 252 | freezeSelection = this.state.freezeSelection; 253 | 254 | //Does not exceed 16 digit phone number limit 255 | if(event.target.value.replace(/\D/g, '').length > 16) { 256 | return; 257 | } 258 | 259 | // if the input is the same as before, must be some special key like enter etc. 260 | if(event.target.value === this.state.formattedNumber) { 261 | return; 262 | } 263 | 264 | // ie hack 265 | if(event.preventDefault) { 266 | event.preventDefault(); 267 | } else { 268 | event.returnValue = false; 269 | } 270 | 271 | if(event.target.value.length > 0) { 272 | // before entering the number in new format, lets check if the dial code now matches some other country 273 | let inputNumber = event.target.value.replace(/\D/g, ''); 274 | 275 | // we don't need to send the whole number to guess the country... only the first 6 characters are enough 276 | // the guess country function can then use memoization much more effectively since the set of input it 277 | // gets has drastically reduced 278 | if(!this.state.freezeSelection || this.state.selectedCountry.dialCode.length > inputNumber.length) { 279 | newSelectedCountry = this.guessSelectedCountry(inputNumber.substring(0, 6), this.state.onlyCountries, this.state.defaultCountry); 280 | freezeSelection = false; 281 | } 282 | // let us remove all non numerals from the input 283 | formattedNumber = this.formatNumber(inputNumber, newSelectedCountry.format); 284 | } 285 | 286 | let caretPosition = event.target.selectionStart; 287 | let oldFormattedText = this.state.formattedNumber; 288 | let diff = formattedNumber.length - oldFormattedText.length; 289 | 290 | this.setState({ 291 | formattedNumber: formattedNumber, 292 | freezeSelection: freezeSelection, 293 | selectedCountry: newSelectedCountry.dialCode.length > 0 ? newSelectedCountry : this.state.selectedCountry 294 | }, function() { 295 | if(isModernBrowser) { 296 | if(diff > 0) { 297 | caretPosition = caretPosition - diff; 298 | } 299 | 300 | if(caretPosition > 0 && oldFormattedText.length >= formattedNumber.length) { 301 | ReactDOM.findDOMNode(this.refs.numberInput).setSelectionRange(caretPosition, caretPosition); 302 | } 303 | } 304 | 305 | if(this.props.onChange) { 306 | this.props.onChange(this.state.formattedNumber); 307 | } 308 | }); 309 | } 310 | 311 | handleInputClick(evt) { 312 | this.setState({showDropDown: false}); 313 | if (this.props.onClick) { 314 | this.props.onClick(evt) 315 | } 316 | } 317 | 318 | handleFlagItemClick(country) { 319 | let currentSelectedCountry = this.state.selectedCountry; 320 | let nextSelectedCountry = find(this.state.onlyCountries, country); 321 | 322 | if(currentSelectedCountry.iso2 !== nextSelectedCountry.iso2) { 323 | // TODO - the below replacement is a bug. It will replace stuff from middle too 324 | let newNumber = this.state.formattedNumber.replace(currentSelectedCountry.dialCode, nextSelectedCountry.dialCode); 325 | let formattedNumber = this.formatNumber(newNumber.replace(/\D/g, ''), nextSelectedCountry.format); 326 | 327 | this.setState({ 328 | showDropDown: false, 329 | selectedCountry: nextSelectedCountry, 330 | freezeSelection: true, 331 | formattedNumber: formattedNumber 332 | }, function() { 333 | this._cursorToEnd(); 334 | if(this.props.onChange) { 335 | this.props.onChange(formattedNumber); 336 | } 337 | }); 338 | } 339 | } 340 | 341 | handleInputFocus(evt) { 342 | // if the input is blank, insert dial code of the selected country 343 | if(ReactDOM.findDOMNode(this.refs.numberInput).value === '+') { 344 | this.setState({formattedNumber: '+' + this.state.selectedCountry.dialCode}, () => setTimeout(this._cursorToEnd, 10)); 345 | } 346 | 347 | if (this.props.onFocus) { 348 | this.props.onFocus(evt) 349 | } 350 | } 351 | 352 | _getHighlightCountryIndex(direction) { 353 | // had to write own function because underscore does not have findIndex. lodash has it 354 | var highlightCountryIndex = this.state.highlightCountryIndex + direction; 355 | 356 | if(highlightCountryIndex < 0 357 | || highlightCountryIndex >= (this.state.onlyCountries.length + this.state.preferredCountries.length)) { 358 | return highlightCountryIndex - direction; 359 | } 360 | 361 | return highlightCountryIndex; 362 | } 363 | 364 | searchCountry() { 365 | const probableCandidate = this._searchCountry(this.state.queryString) || this.state.onlyCountries[0]; 366 | const probableCandidateIndex = findIndex(this.state.onlyCountries, probableCandidate) + 367 | this.state.preferredCountries.length; 368 | 369 | this.scrollTo(this.getElement(probableCandidateIndex), true); 370 | 371 | this.setState({ 372 | queryString: '', 373 | highlightCountryIndex: probableCandidateIndex 374 | }); 375 | } 376 | 377 | handleKeydown(event) { 378 | if(!this.state.showDropDown) { 379 | return; 380 | } 381 | 382 | // ie hack 383 | if(event.preventDefault) { 384 | event.preventDefault(); 385 | } else { 386 | event.returnValue = false; 387 | } 388 | 389 | function _moveHighlight(direction) { 390 | this.setState({ 391 | highlightCountryIndex: this._getHighlightCountryIndex(direction) 392 | }, () => { 393 | this.scrollTo(this.getElement(this.state.highlightCountryIndex), true); 394 | }); 395 | } 396 | 397 | switch(event.which) { 398 | case keys.DOWN: 399 | _moveHighlight(1); 400 | break; 401 | case keys.UP: 402 | _moveHighlight(-1); 403 | break; 404 | case keys.ENTER: 405 | this.handleFlagItemClick(this.state.onlyCountries[this.state.highlightCountryIndex], event); 406 | break; 407 | case keys.ESC: 408 | this.setState({showDropDown: false}, this._cursorToEnd); 409 | break; 410 | default: 411 | if ((event.which >= keys.A && event.which <= keys.Z) || event.which === keys.SPACE) { 412 | this.setState({ 413 | queryString: this.state.queryString + String.fromCharCode(event.which) 414 | }, this.state.debouncedQueryStingSearcher); 415 | } 416 | } 417 | } 418 | 419 | handleInputKeyDown(event) { 420 | if(event.which === keys.ENTER) { 421 | this.props.onEnterKeyPress(event); 422 | } 423 | 424 | if (this.props.onKeyDown) { 425 | this.props.onKeyDown(event) 426 | } 427 | } 428 | 429 | handleClickOutside() { 430 | if(this.state.showDropDown) { 431 | this.setState({ 432 | showDropDown: false 433 | }); 434 | } 435 | } 436 | 437 | getCountryDropDownList() { 438 | let countryDropDownList = map(this.state.preferredCountries.concat(this.state.onlyCountries), (country, index) => { 439 | let itemClasses = classNames({ 440 | country: true, 441 | preferred: country.iso2 === 'us' || country.iso2 === 'gb', 442 | active: country.iso2 === 'us', 443 | highlight: this.state.highlightCountryIndex === index 444 | }); 445 | 446 | let inputFlagClasses = `flag ${country.iso2}`; 447 | 448 | return ( 449 |
  • 457 |
    458 | {country.name} 459 | {'+' + country.dialCode} 460 |
  • 461 | ); 462 | }); 463 | 464 | const dashedLi = (
  • ); 465 | // let's insert a dashed line in between preffered countries and the rest 466 | countryDropDownList.splice(this.state.preferredCountries.length, 0, dashedLi); 467 | 468 | const dropDownClasses = classNames({ 469 | 'country-list': true, 470 | 'hide': !this.state.showDropDown 471 | }); 472 | 473 | return ( 474 |
      475 | {countryDropDownList} 476 |
    477 | ); 478 | } 479 | 480 | handleInputBlur() { 481 | if(typeof this.props.onBlur === 'function') { 482 | this.props.onBlur(this.state.formattedNumber, this.state.selectedCountry); 483 | } 484 | } 485 | 486 | render() { 487 | let arrowClasses = classNames({ 488 | "arrow": true, 489 | "up": this.state.showDropDown 490 | }); 491 | let inputClasses = classNames({ 492 | "form-control": true, 493 | "invalid-number": !this.props.isValid(this.state.formattedNumber.replace(/\D/g, '')) 494 | }); 495 | 496 | let flagViewClasses = classNames({ 497 | "flag-dropdown": true, 498 | "open-dropdown": this.state.showDropDown 499 | }); 500 | 501 | let inputFlagClasses = `flag ${this.state.selectedCountry.iso2}`; 502 | 503 | return ( 504 |
    505 | 517 |
    518 |
    522 |
    523 |
    524 |
    525 |
    526 | {this.state.showDropDown ? this.getCountryDropDownList() : ''} 527 |
    528 |
    529 | ); 530 | } 531 | } 532 | ReactPhoneInput.prototype._searchCountry = memoize(function(queryString){ 533 | if(!queryString || queryString.length === 0) { 534 | return null; 535 | } 536 | // don't include the preferred countries in search 537 | let probableCountries = filter(this.state.onlyCountries, function(country) { 538 | return startsWith(country.name.toLowerCase(), queryString.toLowerCase()); 539 | }, this); 540 | return probableCountries[0]; 541 | }); 542 | 543 | ReactPhoneInput.prototype.guessSelectedCountry = memoize(function(inputNumber, onlyCountries, defaultCountry) { 544 | var secondBestGuess = find(allCountries, {iso2: defaultCountry}) || onlyCountries[0]; 545 | if(trim(inputNumber) !== '') { 546 | var bestGuess = reduce(onlyCountries, function(selectedCountry, country) { 547 | if(startsWith(inputNumber, country.dialCode)) { 548 | if(country.dialCode.length > selectedCountry.dialCode.length) { 549 | return country; 550 | } 551 | if(country.dialCode.length === selectedCountry.dialCode.length && country.priority < selectedCountry.priority) { 552 | return country; 553 | } 554 | } 555 | 556 | return selectedCountry; 557 | }, {dialCode: '', priority: 10001}, this); 558 | } else { 559 | return secondBestGuess; 560 | } 561 | 562 | if(!bestGuess.name) { 563 | return secondBestGuess; 564 | } 565 | 566 | return bestGuess; 567 | }); 568 | 569 | ReactPhoneInput.defaultProps = { 570 | value: '', 571 | autoFormat: true, 572 | onlyCountries: [], 573 | excludeCountries: [], 574 | defaultCountry: allCountries[0].iso2, 575 | isValid: isNumberValid, 576 | flagsImagePath: './flags.png', 577 | onEnterKeyPress: function () {} 578 | }; 579 | 580 | ReactPhoneInput.propTypes = { 581 | value: PropTypes.string, 582 | autoFormat: PropTypes.bool, 583 | defaultCountry: PropTypes.string, 584 | onlyCountries: PropTypes.arrayOf(PropTypes.string), 585 | preferredCountries: PropTypes.arrayOf(PropTypes.string), 586 | onChange: PropTypes.func, 587 | classNames: PropTypes.string, 588 | className: PropTypes.string, 589 | onBlur: PropTypes.func, 590 | onFocus: PropTypes.func, 591 | onClick: PropTypes.func, 592 | onKeyDown: PropTypes.func 593 | }; 594 | 595 | export default ReactPhoneInput; 596 | 597 | if (__DEV__) { 598 | const ReactDOM = require('react-dom'); 599 | ReactDOM.render( 600 | , 601 | document.getElementById('content')); 602 | ReactDOM.render( 603 | , 604 | document.getElementById('content')); 605 | } 606 | -------------------------------------------------------------------------------- /src/react-phone-input-style.less: -------------------------------------------------------------------------------- 1 | .react-tel-input { 2 | position: relative; 3 | width: 100%; 4 | .flag { 5 | width: 16px; 6 | height: 11px; 7 | background: url("./flags.png"); 8 | } 9 | .ad { 10 | background-position: -16px 0; 11 | } 12 | .ae { 13 | background-position: -32px 0; 14 | } 15 | .af { 16 | background-position: -48px 0; 17 | } 18 | .ag { 19 | background-position: -64px 0; 20 | } 21 | .ai { 22 | background-position: -80px 0; 23 | } 24 | .al { 25 | background-position: -96px 0; 26 | } 27 | .am { 28 | background-position: -112px 0; 29 | } 30 | .ao { 31 | background-position: -128px 0; 32 | } 33 | .ar { 34 | background-position: -144px 0; 35 | } 36 | .as { 37 | background-position: -160px 0; 38 | } 39 | .at { 40 | background-position: -176px 0; 41 | } 42 | .au { 43 | background-position: -192px 0; 44 | } 45 | .aw { 46 | background-position: -208px 0; 47 | } 48 | .az { 49 | background-position: -224px 0; 50 | } 51 | .ba { 52 | background-position: -240px 0; 53 | } 54 | .bb { 55 | background-position: 0 -11px; 56 | } 57 | .bd { 58 | background-position: -16px -11px; 59 | } 60 | .be { 61 | background-position: -32px -11px; 62 | } 63 | .bf { 64 | background-position: -48px -11px; 65 | } 66 | .bg { 67 | background-position: -64px -11px; 68 | } 69 | .bh { 70 | background-position: -80px -11px; 71 | } 72 | .bi { 73 | background-position: -96px -11px; 74 | } 75 | .bj { 76 | background-position: -112px -11px; 77 | } 78 | .bm { 79 | background-position: -128px -11px; 80 | } 81 | .bn { 82 | background-position: -144px -11px; 83 | } 84 | .bo { 85 | background-position: -160px -11px; 86 | } 87 | .br { 88 | background-position: -176px -11px; 89 | } 90 | .bs { 91 | background-position: -192px -11px; 92 | } 93 | .bt { 94 | background-position: -208px -11px; 95 | } 96 | .bw { 97 | background-position: -224px -11px; 98 | } 99 | .by { 100 | background-position: -240px -11px; 101 | } 102 | .bz { 103 | background-position: 0 -22px; 104 | } 105 | .ca { 106 | background-position: -16px -22px; 107 | } 108 | .cd { 109 | background-position: -32px -22px; 110 | } 111 | .cf { 112 | background-position: -48px -22px; 113 | } 114 | .cg { 115 | background-position: -64px -22px; 116 | } 117 | .ch { 118 | background-position: -80px -22px; 119 | } 120 | .ci { 121 | background-position: -96px -22px; 122 | } 123 | .ck { 124 | background-position: -112px -22px; 125 | } 126 | .cl { 127 | background-position: -128px -22px; 128 | } 129 | .cm { 130 | background-position: -144px -22px; 131 | } 132 | .cn { 133 | background-position: -160px -22px; 134 | } 135 | .co { 136 | background-position: -176px -22px; 137 | } 138 | .cr { 139 | background-position: -192px -22px; 140 | } 141 | .cu { 142 | background-position: -208px -22px; 143 | } 144 | .cv { 145 | background-position: -224px -22px; 146 | } 147 | .cw { 148 | background-position: -240px -22px; 149 | } 150 | .cy { 151 | background-position: 0 -33px; 152 | } 153 | .cz { 154 | background-position: -16px -33px; 155 | } 156 | .de { 157 | background-position: -32px -33px; 158 | } 159 | .dj { 160 | background-position: -48px -33px; 161 | } 162 | .dk { 163 | background-position: -64px -33px; 164 | } 165 | .dm { 166 | background-position: -80px -33px; 167 | } 168 | .do { 169 | background-position: -96px -33px; 170 | } 171 | .dz { 172 | background-position: -112px -33px; 173 | } 174 | .ec { 175 | background-position: -128px -33px; 176 | } 177 | .ee { 178 | background-position: -144px -33px; 179 | } 180 | .eg { 181 | background-position: -160px -33px; 182 | } 183 | .er { 184 | background-position: -176px -33px; 185 | } 186 | .es { 187 | background-position: -192px -33px; 188 | } 189 | .et { 190 | background-position: -208px -33px; 191 | } 192 | .fi { 193 | background-position: -224px -33px; 194 | } 195 | .fj { 196 | background-position: -240px -33px; 197 | } 198 | .fk { 199 | background-position: 0 -44px; 200 | } 201 | .fm { 202 | background-position: -16px -44px; 203 | } 204 | .fo { 205 | background-position: -32px -44px; 206 | } 207 | .fr, .bl, .mf { 208 | background-position: -48px -44px; 209 | } 210 | .ga { 211 | background-position: -64px -44px; 212 | } 213 | .gb { 214 | background-position: -80px -44px; 215 | } 216 | .gd { 217 | background-position: -96px -44px; 218 | } 219 | .ge { 220 | background-position: -112px -44px; 221 | } 222 | .gf { 223 | background-position: -128px -44px; 224 | } 225 | .gh { 226 | background-position: -144px -44px; 227 | } 228 | .gi { 229 | background-position: -160px -44px; 230 | } 231 | .gl { 232 | background-position: -176px -44px; 233 | } 234 | .gm { 235 | background-position: -192px -44px; 236 | } 237 | .gn { 238 | background-position: -208px -44px; 239 | } 240 | .gp { 241 | background-position: -224px -44px; 242 | } 243 | .gq { 244 | background-position: -240px -44px; 245 | } 246 | .gr { 247 | background-position: 0 -55px; 248 | } 249 | .gt { 250 | background-position: -16px -55px; 251 | } 252 | .gu { 253 | background-position: -32px -55px; 254 | } 255 | .gw { 256 | background-position: -48px -55px; 257 | } 258 | .gy { 259 | background-position: -64px -55px; 260 | } 261 | .hk { 262 | background-position: -80px -55px; 263 | } 264 | .hn { 265 | background-position: -96px -55px; 266 | } 267 | .hr { 268 | background-position: -112px -55px; 269 | } 270 | .ht { 271 | background-position: -128px -55px; 272 | } 273 | .hu { 274 | background-position: -144px -55px; 275 | } 276 | .id { 277 | background-position: -160px -55px; 278 | } 279 | .ie { 280 | background-position: -176px -55px; 281 | } 282 | .il { 283 | background-position: -192px -55px; 284 | } 285 | .in { 286 | background-position: -208px -55px; 287 | } 288 | .io { 289 | background-position: -224px -55px; 290 | } 291 | .iq { 292 | background-position: -240px -55px; 293 | } 294 | .ir { 295 | background-position: 0 -66px; 296 | } 297 | .is { 298 | background-position: -16px -66px; 299 | } 300 | .it { 301 | background-position: -32px -66px; 302 | } 303 | .jm { 304 | background-position: -48px -66px; 305 | } 306 | .jo { 307 | background-position: -64px -66px; 308 | } 309 | .jp { 310 | background-position: -80px -66px; 311 | } 312 | .ke { 313 | background-position: -96px -66px; 314 | } 315 | .kg { 316 | background-position: -112px -66px; 317 | } 318 | .kh { 319 | background-position: -128px -66px; 320 | } 321 | .ki { 322 | background-position: -144px -66px; 323 | } 324 | .km { 325 | background-position: -160px -66px; 326 | } 327 | .kn { 328 | background-position: -176px -66px; 329 | } 330 | .kp { 331 | background-position: -192px -66px; 332 | } 333 | .kr { 334 | background-position: -208px -66px; 335 | } 336 | .kw { 337 | background-position: -224px -66px; 338 | } 339 | .ky { 340 | background-position: -240px -66px; 341 | } 342 | .kz { 343 | background-position: 0 -77px; 344 | } 345 | .la { 346 | background-position: -16px -77px; 347 | } 348 | .lb { 349 | background-position: -32px -77px; 350 | } 351 | .lc { 352 | background-position: -48px -77px; 353 | } 354 | .li { 355 | background-position: -64px -77px; 356 | } 357 | .lk { 358 | background-position: -80px -77px; 359 | } 360 | .lr { 361 | background-position: -96px -77px; 362 | } 363 | .ls { 364 | background-position: -112px -77px; 365 | } 366 | .lt { 367 | background-position: -128px -77px; 368 | } 369 | .lu { 370 | background-position: -144px -77px; 371 | } 372 | .lv { 373 | background-position: -160px -77px; 374 | } 375 | .ly { 376 | background-position: -176px -77px; 377 | } 378 | .ma { 379 | background-position: -192px -77px; 380 | } 381 | .mc { 382 | background-position: -208px -77px; 383 | } 384 | .md { 385 | background-position: -224px -77px; 386 | } 387 | .me { 388 | background-position: -112px -154px; 389 | height: 12px; 390 | } 391 | .mg { 392 | background-position: 0 -88px; 393 | } 394 | .mh { 395 | background-position: -16px -88px; 396 | } 397 | .mk { 398 | background-position: -32px -88px; 399 | } 400 | .ml { 401 | background-position: -48px -88px; 402 | } 403 | .mm { 404 | background-position: -64px -88px; 405 | } 406 | .mn { 407 | background-position: -80px -88px; 408 | } 409 | .mo { 410 | background-position: -96px -88px; 411 | } 412 | .mp { 413 | background-position: -112px -88px; 414 | } 415 | .mq { 416 | background-position: -128px -88px; 417 | } 418 | .mr { 419 | background-position: -144px -88px; 420 | } 421 | .ms { 422 | background-position: -160px -88px; 423 | } 424 | .mt { 425 | background-position: -176px -88px; 426 | } 427 | .mu { 428 | background-position: -192px -88px; 429 | } 430 | .mv { 431 | background-position: -208px -88px; 432 | } 433 | .mw { 434 | background-position: -224px -88px; 435 | } 436 | .mx { 437 | background-position: -240px -88px; 438 | } 439 | .my { 440 | background-position: 0 -99px; 441 | } 442 | .mz { 443 | background-position: -16px -99px; 444 | } 445 | .na { 446 | background-position: -32px -99px; 447 | } 448 | .nc { 449 | background-position: -48px -99px; 450 | } 451 | .ne { 452 | background-position: -64px -99px; 453 | } 454 | .nf { 455 | background-position: -80px -99px; 456 | } 457 | .ng { 458 | background-position: -96px -99px; 459 | } 460 | .ni { 461 | background-position: -112px -99px; 462 | } 463 | .nl, .bq { 464 | background-position: -128px -99px; 465 | } 466 | .no { 467 | background-position: -144px -99px; 468 | } 469 | .np { 470 | background-position: -160px -99px; 471 | } 472 | .nr { 473 | background-position: -176px -99px; 474 | } 475 | .nu { 476 | background-position: -192px -99px; 477 | } 478 | .nz { 479 | background-position: -208px -99px; 480 | } 481 | .om { 482 | background-position: -224px -99px; 483 | } 484 | .pa { 485 | background-position: -240px -99px; 486 | } 487 | .pe { 488 | background-position: 0 -110px; 489 | } 490 | .pf { 491 | background-position: -16px -110px; 492 | } 493 | .pg { 494 | background-position: -32px -110px; 495 | } 496 | .ph { 497 | background-position: -48px -110px; 498 | } 499 | .pk { 500 | background-position: -64px -110px; 501 | } 502 | .pl { 503 | background-position: -80px -110px; 504 | } 505 | .pm { 506 | background-position: -96px -110px; 507 | } 508 | .pr { 509 | background-position: -112px -110px; 510 | } 511 | .ps { 512 | background-position: -128px -110px; 513 | } 514 | .pt { 515 | background-position: -144px -110px; 516 | } 517 | .pw { 518 | background-position: -160px -110px; 519 | } 520 | .py { 521 | background-position: -176px -110px; 522 | } 523 | .qa { 524 | background-position: -192px -110px; 525 | } 526 | .re { 527 | background-position: -208px -110px; 528 | } 529 | .ro { 530 | background-position: -224px -110px; 531 | } 532 | .rs { 533 | background-position: -240px -110px; 534 | } 535 | .ru { 536 | background-position: 0 -121px; 537 | } 538 | .rw { 539 | background-position: -16px -121px; 540 | } 541 | .sa { 542 | background-position: -32px -121px; 543 | } 544 | .sb { 545 | background-position: -48px -121px; 546 | } 547 | .sc { 548 | background-position: -64px -121px; 549 | } 550 | .sd { 551 | background-position: -80px -121px; 552 | } 553 | .se { 554 | background-position: -96px -121px; 555 | } 556 | .sg { 557 | background-position: -112px -121px; 558 | } 559 | .sh { 560 | background-position: -128px -121px; 561 | } 562 | .si { 563 | background-position: -144px -121px; 564 | } 565 | .sk { 566 | background-position: -160px -121px; 567 | } 568 | .sl { 569 | background-position: -176px -121px; 570 | } 571 | .sm { 572 | background-position: -192px -121px; 573 | } 574 | .sn { 575 | background-position: -208px -121px; 576 | } 577 | .so { 578 | background-position: -224px -121px; 579 | } 580 | .sr { 581 | background-position: -240px -121px; 582 | } 583 | .ss { 584 | background-position: 0 -132px; 585 | } 586 | .st { 587 | background-position: -16px -132px; 588 | } 589 | .sv { 590 | background-position: -32px -132px; 591 | } 592 | .sx { 593 | background-position: -48px -132px; 594 | } 595 | .sy { 596 | background-position: -64px -132px; 597 | } 598 | .sz { 599 | background-position: -80px -132px; 600 | } 601 | .tc { 602 | background-position: -96px -132px; 603 | } 604 | .td { 605 | background-position: -112px -132px; 606 | } 607 | .tg { 608 | background-position: -128px -132px; 609 | } 610 | .th { 611 | background-position: -144px -132px; 612 | } 613 | .tj { 614 | background-position: -160px -132px; 615 | } 616 | .tk { 617 | background-position: -176px -132px; 618 | } 619 | .tl { 620 | background-position: -192px -132px; 621 | } 622 | .tm { 623 | background-position: -208px -132px; 624 | } 625 | .tn { 626 | background-position: -224px -132px; 627 | } 628 | .to { 629 | background-position: -240px -132px; 630 | } 631 | .tr { 632 | background-position: 0 -143px; 633 | } 634 | .tt { 635 | background-position: -16px -143px; 636 | } 637 | .tv { 638 | background-position: -32px -143px; 639 | } 640 | .tw { 641 | background-position: -48px -143px; 642 | } 643 | .tz { 644 | background-position: -64px -143px; 645 | } 646 | .ua { 647 | background-position: -80px -143px; 648 | } 649 | .ug { 650 | background-position: -96px -143px; 651 | } 652 | .us { 653 | background-position: -112px -143px; 654 | } 655 | .uy { 656 | background-position: -128px -143px; 657 | } 658 | .uz { 659 | background-position: -144px -143px; 660 | } 661 | .va { 662 | background-position: -160px -143px; 663 | } 664 | .vc { 665 | background-position: -176px -143px; 666 | } 667 | .ve { 668 | background-position: -192px -143px; 669 | } 670 | .vg { 671 | background-position: -208px -143px; 672 | } 673 | .vi { 674 | background-position: -224px -143px; 675 | } 676 | .vn { 677 | background-position: -240px -143px; 678 | } 679 | .vu { 680 | background-position: 0 -154px; 681 | } 682 | .wf { 683 | background-position: -16px -154px; 684 | } 685 | .ws { 686 | background-position: -32px -154px; 687 | } 688 | .ye { 689 | background-position: -48px -154px; 690 | } 691 | .za { 692 | background-position: -64px -154px; 693 | } 694 | .zm { 695 | background-position: -80px -154px; 696 | } 697 | .zw { 698 | background-position: -96px -154px; 699 | } 700 | * { 701 | box-sizing: border-box; 702 | -moz-box-sizing: border-box; 703 | } 704 | .hide { 705 | display: none; 706 | } 707 | .v-hide { 708 | visibility: hidden; 709 | } 710 | input[type=text], 711 | input[type=tel] { 712 | position: relative; 713 | z-index: 0; 714 | margin-top: 0 !important; 715 | margin-bottom: 0 !important; 716 | padding-left: 44px; 717 | margin-left: 0; 718 | background: #FFFFFF; 719 | border: 1px solid #CACACA; 720 | border-radius: 3px; 721 | box-shadow: 0 1px 2px #E3E3E3 inset; 722 | line-height: 25px; 723 | height: 28px; 724 | width: 100%; 725 | &:focus { 726 | border-color: #42bdff; 727 | border-left-color: #cacaca; 728 | } 729 | &.invalid-number { 730 | border: 1px solid #d79f9f; 731 | background-color: #FAF0F0; 732 | border-left-color: #cacaca; 733 | &:focus { 734 | border: 1px solid #d79f9f; 735 | border-left-color: #cacaca; 736 | background-color: #FAF0F0; 737 | } 738 | } 739 | } 740 | .flag-dropdown { 741 | position: absolute; 742 | top: 0; 743 | bottom: 0; 744 | padding: 0; 745 | background-color: #eaeaea; 746 | border: 1px solid #cacaca; 747 | border-radius: 3px 0 0 3px; 748 | &.open-dropdown { 749 | background: #fff; 750 | border-bottom: 0; 751 | border-radius: 3px 0 0 0; 752 | .selected-flag { 753 | background: #fff; 754 | border-radius: 3px 0 0 0; 755 | } 756 | } 757 | &:hover { 758 | cursor: pointer; 759 | .selected-flag { 760 | background-color: #fff; 761 | } 762 | } 763 | } 764 | input[disabled] { 765 | &+.flag-dropdown { 766 | &:hover { 767 | cursor: default; 768 | .selected-flag { 769 | background-color: transparent; 770 | } 771 | } 772 | } 773 | } 774 | .selected-flag { 775 | z-index: 13; 776 | position: relative; 777 | width: 38px; 778 | height: 26px; 779 | padding: 0 0 0 8px; 780 | border-radius: 3px 0 0 3px; 781 | .flag { 782 | position: absolute; 783 | top: 50%; 784 | margin-top: -5px; 785 | } 786 | .arrow { 787 | position: relative; 788 | top: 50%; 789 | margin-top: -2px; 790 | left: 20px; 791 | width: 0; 792 | height: 0; 793 | border-left: 3px solid transparent; 794 | border-right: 3px solid transparent; 795 | border-top: 4px solid #555; 796 | &.up { 797 | border-top: none; 798 | border-bottom: 4px solid #555; 799 | } 800 | } 801 | } 802 | .country-list { 803 | list-style: none; 804 | position: absolute; 805 | z-index: 15; 806 | padding: 0; 807 | margin: -1px 0 0 -1px; 808 | box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2); 809 | background-color: white; 810 | border: 1px solid #ccc; 811 | width: 400px; 812 | max-height: 200px; 813 | overflow-y: scroll; 814 | border-radius: 0 0 3px 3px; 815 | top: 26px; 816 | .flag { 817 | display: inline-block; 818 | } 819 | .divider { 820 | padding-bottom: 5px; 821 | margin-bottom: 5px; 822 | border-bottom: 1px solid #ccc; 823 | } 824 | .country { 825 | padding: 5px 10px; 826 | .dial-code { 827 | color: #999; 828 | } 829 | &:hover { 830 | background-color: #e8f7fe; 831 | } 832 | &.highlight { 833 | background-color: #c7e2f1; 834 | } 835 | } 836 | .flag { 837 | margin-right: 6px; 838 | margin-top: 2px; 839 | } 840 | .country-name { 841 | margin-right: 6px; 842 | } 843 | } 844 | } 845 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var merge = require('webpack-merge'); 4 | 5 | var TARGET = process.env.TARGET; 6 | var ROOT_PATH = path.resolve(__dirname); 7 | var nodeModulesDir = path.join(ROOT_PATH, 'node_modules'); 8 | 9 | //Common configuration settings 10 | var common = { 11 | entry: path.resolve(ROOT_PATH, 'src/index.js'), 12 | resolve: { 13 | extensions: ['', '.js', '.jsx'], 14 | modulesDirectories: ['node_modules'] 15 | }, 16 | output: { 17 | path: path.resolve(ROOT_PATH, 'dist'), 18 | filename: 'index.js' 19 | }, 20 | module: { 21 | loaders: [ 22 | { 23 | test: /\.(js|jsx)$/, 24 | loader: 'babel', 25 | include: path.resolve(ROOT_PATH, 'src') 26 | }, 27 | { 28 | test: /\.png.*$/, 29 | loaders: ['url-loader?limit=100000&mimetype=image/png'], 30 | exclude: /node_modules/ 31 | }, 32 | { 33 | test: /\.less$/, 34 | loader: "style!css!less" 35 | } 36 | ] 37 | } 38 | }; 39 | 40 | //Development configuration settings 41 | if (TARGET === 'dev') { 42 | module.exports = merge(common, { 43 | devtool: 'eval', 44 | // module: { 45 | // loaders: [ 46 | // { 47 | // test: /\.jsx?$/, 48 | // loaders: ['react-hot', 'babel?stage=1'], 49 | // include: path.resolve(ROOT_PATH, 'src') 50 | // } 51 | // ] 52 | // }, 53 | devServer: { 54 | publicPath: 'http://localhost:8181/', 55 | port: '8181', 56 | host: '0.0.0.0', 57 | colors: true, 58 | historyApiFallback: true, 59 | hot: true, 60 | inline: true, 61 | progress: true, 62 | contentBase: 'dist' 63 | }, 64 | plugins: [ 65 | new webpack.HotModuleReplacementPlugin(), 66 | new webpack.DefinePlugin({ 67 | 'process.env': { 68 | 'NODE_ENV': JSON.stringify('development') 69 | }, 70 | '__DEV__': true 71 | }) 72 | ] 73 | }); 74 | } 75 | 76 | 77 | //Production configuration settings 78 | if (TARGET === 'build') { 79 | module.exports = merge(common, { 80 | entry: { 81 | 'react-phone-input': path.resolve(ROOT_PATH, 'src/index.js') 82 | }, 83 | output: { 84 | path: path.resolve(ROOT_PATH, 'dist'), 85 | filename: 'index.js', 86 | library: 'ReactPhoneInput', 87 | libraryTarget: 'umd' 88 | }, 89 | externals: [{ 90 | "lodash": "lodash", 91 | "react": { 92 | root: 'React', 93 | commonjs2: 'react', 94 | commonjs: 'react', 95 | amd: 'react' 96 | } 97 | }], 98 | plugins: [ 99 | new webpack.DefinePlugin({ 100 | 'process.env': { 101 | 'NODE_ENV': JSON.stringify('production') 102 | }, 103 | '__DEV__': false 104 | }), 105 | new webpack.optimize.UglifyJsPlugin({ 106 | compress: { 107 | warnings: false 108 | } 109 | }), 110 | new webpack.optimize.DedupePlugin() 111 | ] 112 | }); 113 | } 114 | 115 | --------------------------------------------------------------------------------