simple-phone-mask
Version:
A lightweight and customizable phone number input mask with country flags, search, validation and dark theme.
1 lines • 13.6 kB
JavaScript
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SimplePhoneMask=t():e.SimplePhoneMask=t()}(this,function(){return function(){"use strict";var e={d:function(t,n){for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var t={};e.d(t,{default:function(){return s}});const n={UA:{name:"Ukraine",phoneCode:"+380",flag:"https://flagcdn.com/ua.svg",mask:"(__) ___-__-__"},US:{name:"United States",phoneCode:"+1",flag:"https://flagcdn.com/us.svg",mask:"(___) ___-____"},GB:{name:"United Kingdom",phoneCode:"+44",flag:"https://flagcdn.com/gb.svg",mask:"____ ______"},DE:{name:"Germany",phoneCode:"+49",flag:"https://flagcdn.com/de.svg",mask:"(__) ____ ____"},FR:{name:"France",phoneCode:"+33",flag:"https://flagcdn.com/fr.svg",mask:"_ __ __ __ __"},PL:{name:"Poland",phoneCode:"+48",flag:"https://flagcdn.com/pl.svg",mask:"___-___-___"},IT:{name:"Italy",phoneCode:"+39",flag:"https://flagcdn.com/it.svg",mask:"(___) ____-____"},ES:{name:"Spain",phoneCode:"+34",flag:"https://flagcdn.com/es.svg",mask:"(___) ___-___"},NL:{name:"Netherlands",phoneCode:"+31",flag:"https://flagcdn.com/nl.svg",mask:"__ ________"},CZ:{name:"Czech Republic",phoneCode:"+420",flag:"https://flagcdn.com/cz.svg",mask:"___-___-___"},RU:{name:"Russia",phoneCode:"+7",flag:"https://flagcdn.com/ru.svg",mask:"(___) ___-__-__"},CN:{name:"China",phoneCode:"+86",flag:"https://flagcdn.com/cn.svg",mask:"(___) ____-____"},JP:{name:"Japan",phoneCode:"+81",flag:"https://flagcdn.com/jp.svg",mask:"(__) ____-____"},KR:{name:"South Korea",phoneCode:"+82",flag:"https://flagcdn.com/kr.svg",mask:"(__) ____-____"},IN:{name:"India",phoneCode:"+91",flag:"https://flagcdn.com/in.svg",mask:"(___) ___-____"},BR:{name:"Brazil",phoneCode:"+55",flag:"https://flagcdn.com/br.svg",mask:"(___) ____-____"},MX:{name:"Mexico",phoneCode:"+52",flag:"https://flagcdn.com/mx.svg",mask:"(___) ___-____"},AU:{name:"Australia",phoneCode:"+61",flag:"https://flagcdn.com/au.svg",mask:"_ ____ ____"},NZ:{name:"New Zealand",phoneCode:"+64",flag:"https://flagcdn.com/nz.svg",mask:"_ ___ ____"},CA:{name:"Canada",phoneCode:"+1",flag:"https://flagcdn.com/ca.svg",mask:"(___) ___-____"},IE:{name:"Ireland",phoneCode:"+353",flag:"https://flagcdn.com/ie.svg",mask:"(__) ___-____"},SE:{name:"Sweden",phoneCode:"+46",flag:"https://flagcdn.com/se.svg",mask:"(__) ___-____"},NO:{name:"Norway",phoneCode:"+47",flag:"https://flagcdn.com/no.svg",mask:"__ __ __ __"},DK:{name:"Denmark",phoneCode:"+45",flag:"https://flagcdn.com/dk.svg",mask:"__ __ __ __"},FI:{name:"Finland",phoneCode:"+358",flag:"https://flagcdn.com/fi.svg",mask:"(__) ___-____"},AT:{name:"Austria",phoneCode:"+43",flag:"https://flagcdn.com/at.svg",mask:"(__) ___-____"},CH:{name:"Switzerland",phoneCode:"+41",flag:"https://flagcdn.com/ch.svg",mask:"(__) ___-____"},BE:{name:"Belgium",phoneCode:"+32",flag:"https://flagcdn.com/be.svg",mask:"(__) ___-____"},PT:{name:"Portugal",phoneCode:"+351",flag:"https://flagcdn.com/pt.svg",mask:"___-___-___"},GR:{name:"Greece",phoneCode:"+30",flag:"https://flagcdn.com/gr.svg",mask:"(___) ___-____"},HU:{name:"Hungary",phoneCode:"+36",flag:"https://flagcdn.com/hu.svg",mask:"(__) ___-____"},RO:{name:"Romania",phoneCode:"+40",flag:"https://flagcdn.com/ro.svg",mask:"(__) ___-____"},BG:{name:"Bulgaria",phoneCode:"+359",flag:"https://flagcdn.com/bg.svg",mask:"(__) ___-____"},HR:{name:"Croatia",phoneCode:"+385",flag:"https://flagcdn.com/hr.svg",mask:"(__) ___-____"},RS:{name:"Serbia",phoneCode:"+381",flag:"https://flagcdn.com/rs.svg",mask:"(__) ___-____"},SK:{name:"Slovakia",phoneCode:"+421",flag:"https://flagcdn.com/sk.svg",mask:"(__) ___-____"},SI:{name:"Slovenia",phoneCode:"+386",flag:"https://flagcdn.com/si.svg",mask:"(__) ___-____"},IL:{name:"Israel",phoneCode:"+972",flag:"https://flagcdn.com/il.svg",mask:"___-___-____"},TR:{name:"Turkey",phoneCode:"+90",flag:"https://flagcdn.com/tr.svg",mask:"(___) ___-____"},AE:{name:"United Arab Emirates",phoneCode:"+971",flag:"https://flagcdn.com/ae.svg",mask:"(__) ___-____"},SA:{name:"Saudi Arabia",phoneCode:"+966",flag:"https://flagcdn.com/sa.svg",mask:"(__) ___-____"},EG:{name:"Egypt",phoneCode:"+20",flag:"https://flagcdn.com/eg.svg",mask:"(___) ___-____"},ZA:{name:"South Africa",phoneCode:"+27",flag:"https://flagcdn.com/za.svg",mask:"(__) ___-____"},SG:{name:"Singapore",phoneCode:"+65",flag:"https://flagcdn.com/sg.svg",mask:"____-____"},MY:{name:"Malaysia",phoneCode:"+60",flag:"https://flagcdn.com/my.svg",mask:"(__) ___-____"},TH:{name:"Thailand",phoneCode:"+66",flag:"https://flagcdn.com/th.svg",mask:"(__) ___-____"},VN:{name:"Vietnam",phoneCode:"+84",flag:"https://flagcdn.com/vn.svg",mask:"(___) ____-___"},PH:{name:"Philippines",phoneCode:"+63",flag:"https://flagcdn.com/ph.svg",mask:"(___) ___-____"},ID:{name:"Indonesia",phoneCode:"+62",flag:"https://flagcdn.com/id.svg",mask:"(___) ___-____"},AR:{name:"Argentina",phoneCode:"+54",flag:"https://flagcdn.com/ar.svg",mask:"(___) ___-____"},CL:{name:"Chile",phoneCode:"+56",flag:"https://flagcdn.com/cl.svg",mask:"(__) ___-____"},CO:{name:"Colombia",phoneCode:"+57",flag:"https://flagcdn.com/co.svg",mask:"(___) ___-____"},PE:{name:"Peru",phoneCode:"+51",flag:"https://flagcdn.com/pe.svg",mask:"(___) ___-____"}};var a=class{constructor(e,t={}){this.selector=e;this.options={countryCode:"UA",maskPattern:null,showFlag:!0,allowCountrySelect:!0,detectIP:!1,darkTheme:!1,showSearch:!0,validate:!1,onValidate:null,preferredCountries:[],errorMessage:"Please enter a complete phone number.",successMessage:"✓ Looks good!",...t};let a=this._resolveCountry(this.options.countryCode);a||(a=n.UA),this._applyCountry(a),this.createMask=this.createMask.bind(this),this.handleClick=this.handleClick.bind(this),this.handleKeyDown=this.handleKeyDown.bind(this),this.handleSelect=this.handleSelect.bind(this),this.handleCountrySelect=this.handleCountrySelect.bind(this),this.createDropdown=this.createDropdown.bind(this),this._handleBlurValidation=this._handleBlurValidation.bind(this),this.initialize()}_resolveCountry(e){if(e)return e.startsWith("+")?Object.values(n).find(t=>t.phoneCode===e):n[e.toUpperCase()]}_applyCountry(e){this.countryCode=e.phoneCode.replace("+",""),this.maskPattern=this.options.maskPattern||e.mask,this.prefixLength=this.countryCode.length+1,this.currentCountry=e}_isComplete(e){const t=(this.maskPattern.match(/_/g)||[]).length,n=this.countryCode.length+t;return e.value.replace(/\D/g,"").length===n}_runValidation(e,t){const n=e.value==="+"+this.countryCode||""===e.value.trim(),a=this._isComplete(e);return e.classList.remove("spm-valid","spm-invalid"),n||e.classList.add(a?"spm-valid":"spm-invalid"),t&&(n||a?!n&&a?(t.textContent=this.options.successMessage,t.className="spm-validation-message spm-valid-msg",t.style.display="block"):t.style.display="none":(t.textContent=this.options.errorMessage,t.className="spm-validation-message",t.style.display="block")),"function"==typeof this.options.onValidate&&this.options.onValidate(a,e.value),a}_handleBlurValidation(e){if(!this.options.validate)return;const t=e.target,n=t._spmMsgEl||null;this._runValidation(t,n)}setCursorPosition(e,t,n=!1){if(n&&t.focus(),t.setSelectionRange)t.setSelectionRange(e,e);else if(t.createTextRange){const n=t.createTextRange();n.collapse(!0),n.moveEnd("character",e),n.moveStart("character",e),n.select()}}handleClick(e){const t=e.target;t.selectionStart<this.prefixLength+1&&this.setCursorPosition(this.prefixLength+1,t,!0)}handleKeyDown(e){const t=e.target,n=t.selectionStart;("Backspace"===e.key||"ArrowLeft"===e.key||"ArrowUp"===e.key)&&n<=this.prefixLength&&(e.preventDefault(),this.setCursorPosition(this.prefixLength+1,t,!0));const a=t.value.replace(/\D/g,"").length-this.countryCode.length;"ArrowUp"===e.key&&a>1&&e.preventDefault()}handleSelect(e){const t=e.target;t.selectionStart<this.prefixLength+1&&this.setCursorPosition(this.prefixLength+1,t,!0)}createMask(e){const t=e.target,n="+"+this.countryCode+" "+this.maskPattern;let a=0,s=t.value.replace(/\D/g,"");t.value=n.replace(/./g,e=>/[_\d]/.test(e)&&a<s.length?s.charAt(a++):a>=s.length?"":e),"blur"===e.type?t.value.length<=this.prefixLength+1&&(t.value="+"+this.countryCode):this.setCursorPosition(t.value.length,t)}_buildOption(e,t,n,a){const s=document.createElement("div");return s.className="spm-dropdown-option"+(a?" spm-option-selected":""),s.dataset.name=t.name.toLowerCase(),s.dataset.code=t.phoneCode,s.innerHTML=`\n\t\t\t<img class="spm-flag-image" src="${t.flag}" alt="${t.name}">\n\t\t\t<span class="spm-country-name">${t.name}</span>\n\t\t\t<span class="spm-country-code">${t.phoneCode}</span>\n\t\t`,s.addEventListener("click",()=>this.handleCountrySelect(t,e,n)),s}createDropdown(e){if(!this.options.showFlag)return;const t=document.createElement("div");t.className="spm-wrapper"+(this.options.darkTheme?" spm-dark":""),e.parentNode.insertBefore(t,e),t.appendChild(e),e.classList.add("spm-input");const a=document.createElement("div");if(a.className="spm-flag-button "+(this.options.allowCountrySelect?"spm-flag-button--selectable":"spm-flag-button--non-selectable"),a.innerHTML=`<img class="spm-flag-image" src="${this.currentCountry.flag}" alt="${this.currentCountry.name}">`,t.appendChild(a),!this.options.allowCountrySelect)return;const s=document.createElement("div");if(s.className="spm-dropdown",this.options.showSearch){const e=document.createElement("div");e.className="spm-search-wrapper";const t=document.createElement("input");t.type="text",t.className="spm-search-input",t.placeholder="Search country…",t.addEventListener("input",()=>{const e=t.value.toLowerCase().trim();o.querySelectorAll(".spm-dropdown-option").forEach(t=>{const n=t.dataset.name.includes(e),a=t.dataset.code.includes(e);t.style.display=n||a?"":"none"});const n=o.querySelector(".spm-no-results"),a=[...o.querySelectorAll(".spm-dropdown-option")].some(e=>"none"!==e.style.display);n&&(n.style.display=a?"none":"")}),e.appendChild(t),s.appendChild(e),a.addEventListener("click",()=>{"block"===s.style.display&&setTimeout(()=>t.focus(),0)})}const o=document.createElement("div");o.className="spm-options-list";const _=(this.options.preferredCountries||[]).map(e=>e.toUpperCase());if(_.length>0){_.forEach(t=>{const a=n[t];if(!a)return;const s=a.phoneCode===this.currentCountry.phoneCode;o.appendChild(this._buildOption(t,a,e,s))});const t=document.createElement("hr");t.className="spm-divider",o.appendChild(t)}Object.entries(n).forEach(([t,n])=>{if(_.includes(t))return;const a=n.phoneCode===this.currentCountry.phoneCode;o.appendChild(this._buildOption(t,n,e,a))});const l=document.createElement("div");l.className="spm-no-results",l.textContent="No countries found.",l.style.display="none",o.appendChild(l),s.appendChild(o),t.appendChild(s),a.addEventListener("click",()=>{const e="block"===s.style.display;s.style.display=e?"none":"block"}),document.addEventListener("click",e=>{t.contains(e.target)||(s.style.display="none")})}handleCountrySelect(e,t,n){this._applyCountry(e);n.parentNode.querySelector(".spm-flag-button").innerHTML=`<img class="spm-flag-image" src="${e.flag}" alt="${e.name}">`;const a=n.parentNode.querySelector(".spm-options-list");a&&a.querySelectorAll(".spm-dropdown-option").forEach(t=>{t.classList.toggle("spm-option-selected",t.dataset.code===e.phoneCode)});const s=n.parentNode.querySelector(".spm-dropdown");s&&(s.style.display="none");const o=n.parentNode.querySelector(".spm-search-input");o&&(o.value="",o.dispatchEvent(new Event("input"))),n.value="+"+this.countryCode,this.createMask({type:"input",target:n}),n.focus()}async initialize(){if(this.options.detectIP)try{const e=await this.detectCountryByIP();this.options.countryCode=e;const t=n[e];t&&this._applyCountry(t)}catch(e){console.error("Failed to detect country by IP:",e)}document.querySelectorAll(this.selector).forEach(e=>{if(this.options.showFlag&&this.createDropdown(e),this.options.validate){const t=document.createElement("span");t.className="spm-validation-message";const n=e.closest(".spm-wrapper")||e;n.parentNode.insertBefore(t,n.nextSibling),e._spmMsgEl=t}e.addEventListener("input",this.createMask),e.addEventListener("focus",this.createMask),e.addEventListener("blur",this.createMask),e.addEventListener("blur",this._handleBlurValidation),e.addEventListener("click",this.handleClick),e.addEventListener("keydown",this.handleKeyDown),e.addEventListener("select",this.handleSelect),e.value="+"+this.countryCode,this.createMask({type:"input",target:e})})}async detectCountryByIP(){try{const e=await fetch("https://ipapi.co/json/",{cache:"no-store"});return(await e.json()).country_code}catch(e){return console.error("Error detecting country by IP:",e),this.options.countryCode}}validate(e){const t=e||this.selector,n=document.querySelectorAll(t);let a=!0;return n.forEach(e=>{this._runValidation(e,e._spmMsgEl||null)||(a=!1)}),a}destroy(){document.querySelectorAll(this.selector).forEach(e=>{e.removeEventListener("input",this.createMask),e.removeEventListener("focus",this.createMask),e.removeEventListener("blur",this.createMask),e.removeEventListener("blur",this._handleBlurValidation),e.removeEventListener("click",this.handleClick),e.removeEventListener("keydown",this.handleKeyDown),e.removeEventListener("select",this.handleSelect)})}};e.g.SimplePhoneMask=a;var s=a;return t=t.default}()});