@desource/phone-mask-vue 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1441 @@
1
+ var lib = (function(exports, vue) {
2
+ "use strict";
3
+ const M = { AC: "+247 #####", AD: ["+376 ### ###", "+376 #### ####"], AE: ["+971 # ### ####", "+971 ## ### ####", "+971 ### ######", "+971 ### # #####"], AF: "+93 ## ### ####", AG: "+1 ###-###-####", AI: "+1 ###-###-####", AL: ["+355 ## ### ###", "+355 ## ### ####", "+355 ### ####", "+355 ### ###", "+355 ### #####"], AM: ["+374 ## ######", "+374 ### ## ###"], AO: "+244 ### ### ###", AR: ["+54 ## ####-####", "+54 # ## ####-####", "+54 ###-###-####"], AS: "+1 ###-###-####", AT: ["+43 # #########", "+43 ### ######"], AU: ["+61 # #### ####", "+61 ### ### ###", "+61 #### ### ###", "+61 ## ### ##"], AW: "+297 ### ####", AX: ["+358 ## #######", "+358 ### ######"], AZ: ["+994 ## ### ## ##", "+994 ### ## ## ##"], BA: ["+387 ## ###-###", "+387 ## ### ###"], BB: "+1 ###-###-####", BD: ["+880 #-#######", "+880 ####-######", "+880 ###-#######"], BE: ["+32 ## ## ## ##", "+32 ### ## ## ##", "+32 ### ## ###"], BF: "+226 ## ## ## ##", BG: ["+359 # ### ###", "+359 ## ### ###", "+359 ### ## ###"], BH: "+973 #### ####", BI: "+257 ## ## ## ##", BJ: ["+229 ## ## ## ## ##", "+229 ## ## ## ##"], BL: "+590 ### ## ## ##", BM: "+1 ###-###-####", BN: "+673 ### ####", BO: ["+591 # #######", "+591 ########", "+591 ### ## ####"], BQ: "+599 ### ####", BR: ["+55 ## ####-####", "+55 ## #####-####", "+55 ### ## ####", "+55 ####-####"], BS: "+1 ###-###-####", BT: ["+975 # ### ###", "+975 ## ## ## ##"], BW: ["+267 ### ####", "+267 ## ### ###", "+267 #### ### ###", "+267 ## #####"], BY: ["+375 ### ##-##-##", "+375 ## ###-##-##", "+375 ### ### ####"], BZ: ["+501 ###-####", "+501 #-###-####-###"], CA: "+1 ###-###-####", CC: ["+61 # #### ####", "+61 ### ### ###", "+61 #### ### ###"], CD: ["+243 ## #####", "+243 ### ### ###"], CF: "+236 ## ## ## ##", CG: ["+242 ## ### ####", "+242 # #### ####"], CH: ["+41 ## ### ## ##", "+41 ### ### ###"], CI: ["+225 ## ## # #####", "+225 ## ## ## ####"], CK: "+682 ## ###", CL: ["+56 ### ### ###", "+56 # #### ####", "+56 ### ### ####", "+56 ## ### ####"], CM: ["+237 # ## ## ## ##", "+237 ## ## ## ##"], CN: ["+86 ## #### ####", "+86 ### #### ####", "+86 ### ### ####", "+86 ########"], CO: ["+57 ### #######", "+57 # ### #######"], CR: ["+506 #### ####", "+506 ###-###-####"], CU: ["+53 # #######", "+53 ### #######"], CV: "+238 ### ## ##", CW: ["+599 # ### ####", "+599 ### ####"], CX: ["+61 # #### ####", "+61 ### ### ###", "+61 #### ### ###"], CY: "+357 ## ######", CZ: "+420 ### ### ###", DE: ["+49 ## ######", "+49 #### #######", "+49 ### ##########", "+49 ### # ######", "+49 ### # ####", "+49 ### #### ####", "+49 ########"], DJ: "+253 ## ## ## ##", DK: "+45 ## ## ## ##", DM: "+1 ###-###-####", DO: "+1 ###-###-####", DZ: ["+213 ## ## ## ##", "+213 ### ## ## ##", "+213 ## ### ## ##"], EC: ["+593 #-###-####", "+593 ## ### ####", "+593 #### ### ####"], EE: ["+372 ### ####", "+372 #### ####", "+372 ## ## ####"], EG: ["+20 # ########", "+20 ## ########", "+20 ### ### ####"], EH: ["+212 ####-#####", "+212 ###-######", "+212 ##-#######"], ER: "+291 # ### ###", ES: ["+34 ### ## ## ##", "+34 ### ### ###"], ET: "+251 ## ### ####", FI: ["+358 ## #######", "+358 ### ######"], FJ: ["+679 ### ####", "+679 #### ### ####"], FK: "+500 #####", FM: "+691 ### ####", FO: "+298 ######", FR: ["+33 # ## ## ## ##", "+33 ### ## ## ##"], GA: "+241 ## ## ## ##", GB: ["+44 ### ### ####", "+44 #### ######", "+44 ## #### ####"], GD: "+1 ###-###-####", GE: ["+995 ## ### ## ##", "+995 ### ## ## ##", "+995 ### ### ###"], GF: "+594 ### ## ## ##", GG: ["+44 #### ######", "+44 ### ### ####", "+44 ## #### ####"], GH: ["+233 ## ### ####", "+233 ### #####"], GI: ["+350 ### #####", "+350 ########"], GL: "+299 ## ## ##", GM: "+220 ### ####", GN: ["+224 ## ## ## ##", "+224 ### ## ## ##"], GP: "+590 ### ## ## ##", GQ: ["+240 ### ### ###", "+240 ### ######"], GR: ["+30 ## #### ####", "+30 ### ### ####"], GT: ["+502 #### ####", "+502 #### ### ####"], GU: "+1 ###-###-####", GW: ["+245 ### ### ###", "+245 ### ####"], GY: "+592 ### ####", HK: ["+852 #### ####", "+852 ### ### ###", "+852 ### ## ### ###"], HN: ["+504 ####-####", "+504 ###########"], HR: ["+385 # #### ###", "+385 ## ### ####", "+385 ### ### ###", "+385 ## ## ###", "+385 ## ### ###"], HT: "+509 ## ## ####", HU: ["+36 # ### ####", "+36 ## ### ####", "+36 ## ### ###"], ID: ["+62 ## #######", "+62 ###-###-###", "+62 ### #######", "+62 ### # ### ###", "+62 ### ### ####"], IE: ["+353 ## #####", "+353 ## ### ####", "+353 #### ### ###", "+353 ### ### ###"], IL: ["+972 #-###-####", "+972 ##-###-####", "+972 #-###-###-###"], IM: ["+44 #### ######", "+44 ### ### ####", "+44 ## #### ####"], IN: ["+91 ##### #####", "+91 #### ## ####", "+91 #### ### ### ###", "+91 #### ### ####"], IO: "+246 ### ####", IQ: ["+964 # ### ####", "+964 ### ### ####"], IR: ["+98 ## #### ####", "+98 ### ### ####"], IS: "+354 ### ####", IT: ["+39 ## #### ####", "+39 ### ### ####", "+39 ### ### ###"], JE: ["+44 #### ######", "+44 ### ### ####", "+44 ## #### ####"], JM: "+1 ###-###-####", JO: ["+962 # ### ####", "+962 # #### ####", "+962 ### #####", "+962 ## #######"], JP: ["+81 #-####-####", "+81 ##-####-####", "+81 ###-###-###", "+81 ##-###-####"], KE: ["+254 ## #######", "+254 ### ######", "+254 ### ### ###"], KG: ["+996 ### ### ###", "+996 ### ### # ##"], KH: ["+855 ## ### ###", "+855 #### ### ###"], KI: ["+686 #####", "+686 ########"], KM: "+269 ### ## ##", KN: "+1 ###-###-####", KP: ["+850 # ### ####", "+850 ### ### ####"], KR: ["+82 #-###-####", "+82 ##-####-####", "+82 ##-###-####"], KW: ["+965 #### ####", "+965 ### #####", "+965 #### ###"], KY: "+1 ###-###-####", KZ: ["+7 ##### # ## ##", "+7 ### ### ####", "+7 ### ###-##-##"], LA: ["+856 ## ### ###", "+856 ## ## ### ###"], LB: ["+961 # ### ###", "+961 ## ### ###"], LC: "+1 ###-###-####", LI: ["+423 ### ## ##", "+423 ### ### ###"], LK: ["+94 ### ### ###", "+94 ## ### ####"], LR: ["+231 ## ### ###", "+231 ## ### ####"], LS: "+266 #### ####", LT: ["+370 ### #####", "+370 ### ## ###"], LU: ["+352 ## ## ## ##", "+352 ### ### ###", "+352 ### ## ###"], LV: "+371 ## ### ###", LY: "+218 ##-#######", MA: ["+212 ###-######", "+212 ##-#######", "+212 ####-#####"], MC: ["+377 ## ## ## ##", "+377 # ## ## ## ##"], MD: ["+373 ## ### ###", "+373 ### ## ###", "+373 ### #####"], ME: "+382 ## ### ###", MF: "+590 ### ## ## ##", MG: "+261 ## ## ### ##", MH: "+692 ###-####", MK: ["+389 # ### ####", "+389 ## ### ###", "+389 ### # ## ##"], ML: "+223 ## ## ## ##", MM: ["+95 # ### ###", "+95 # ### ####", "+95 ### ### ####"], MN: "+976 #### ####", MO: ["+853 #### ####", "+853 #### ###"], MP: "+1 ###-###-####", MQ: "+596 ### ## ## ##", MR: "+222 ## ## ## ##", MS: "+1 ###-###-####", MT: "+356 #### ####", MU: ["+230 #### ####", "+230 ### ####"], MV: ["+960 ###-####", "+960 ### ### ####"], MW: ["+265 # ### ###", "+265 ### ## ## ##"], MX: "+52 ### ### ####", MY: ["+60 #-#### ####", "+60 ##-### ####", "+60 #-###-##-####", "+60 ###-### ####"], MZ: ["+258 ## ### ###", "+258 ## ### ####", "+258 ### ### ###"], NA: ["+264 ## ### ###", "+264 ## ### ####", "+264 ### ### ###"], NC: "+687 ##.##.##", NE: ["+227 ## ## ## ##", "+227 ## ### ###"], NF: ["+672 ## ####", "+672 # #####"], NG: ["+234 #### ## ####", "+234 ### ### ####", "+234 ### #### ####"], NI: "+505 #### ####", NL: ["+31 ## ### ####", "+31 # ########", "+31 ### ####", "+31 ## #######"], NO: ["+47 ## ## ## ##", "+47 ### ## ###"], NP: ["+977 #-#######", "+977 ###-#######", "+977 ###########"], NR: "+674 ### ####", NU: ["+683 ####", "+683 ### ####"], NZ: ["+64 # ### ####", "+64 ## ### ####", "+64 ### ### ###"], OM: ["+968 ## ######", "+968 #### ####", "+968 ### #####"], PA: ["+507 ###-####", "+507 ####-####"], PE: ["+51 # #######", "+51 ### ### ###", "+51 ### #####"], PF: ["+689 ## ## ## ##", "+689 ### ## ## ##"], PG: ["+675 ### ####", "+675 #### ####"], PH: ["+63 # #### ####", "+63 ### ### ####", "+63 #### # ### ####"], PK: ["+92 ## ########", "+92 ### #######", "+92 ### ### ##", "+92 #### #####"], PL: ["+48 ## ### ## ##", "+48 ### ### ###"], PM: ["+508 ## ## ##", "+508 ### ## ## ##"], PR: "+1 ###-###-####", PS: ["+970 # ### ####", "+970 ### ### ###", "+970 #### ### ###"], PT: ["+351 ## ### ####", "+351 ### ### ###"], PW: "+680 ### ####", PY: ["+595 ## ### ####", "+595 ### ######", "+595 #### ### ####"], QA: ["+974 #### ####", "+974 ### ####"], RE: "+262 ### ## ## ##", RO: ["+40 ## ### ####", "+40 ### ### ###"], RS: ["+381 ## ######", "+381 ## #######", "+381 ### #####"], RU: "+7 ### ###-##-##", RW: "+250 ### ### ###", SA: ["+966 ## ### ####", "+966 ### ### ####", "+966 #### #####"], SB: ["+677 #####", "+677 ## #####"], SC: ["+248 # ### ###", "+248 #######"], SD: "+249 ## ### ####", SE: ["+46 # ## ## ##", "+46 ## ### ## ##", "+46 ## ## ## ##", "+46 ### ## ## ###"], SG: ["+65 #### ####", "+65 #### ### ####"], SH: "+290 #####", SI: ["+386 # ### ## ##", "+386 ## ### ###", "+386 ## ######", "+386 ### #####"], SJ: ["+47 ## ## ## ##", "+47 ### ## ###"], SK: ["+421 #/### ### ##", "+421 ### ### ###", "+421 #######"], SL: "+232 ## ######", SM: ["+378 #### ######", "+378 ## ## ## ##"], SN: ["+221 ## ### ## ##", "+221 ### ## ## ##"], SO: ["+252 # ######", "+252 # #######"], SR: ["+597 ###-###", "+597 ###-####", "+597 ##-##-##"], SS: "+211 ### ### ###", ST: "+239 ### ####", SV: ["+503 #### ####", "+503 ### ####"], SX: "+1 ###-###-####", SY: ["+963 ## ### ####", "+963 ### ### ###"], SZ: ["+268 #### ####", "+268 ##### ####"], TA: "+290 ####", TC: "+1 ###-###-####", TD: "+235 ## ## ## ##", TG: "+228 ## ## ## ##", TH: ["+66 # ### ####", "+66 ## ### ####", "+66 #### ### ###"], TJ: ["+992 ### ## ####", "+992 ## ### ####"], TK: "+690 ####", TL: ["+670 ### ####", "+670 #### ####"], TM: ["+993 ## ##-##-##", "+993 ## ######"], TN: "+216 ## ### ###", TO: ["+676 ##-###", "+676 ### ####", "+676 #### ###"], TR: ["+90 ### ### ## ##", "+90 ### ### ####"], TT: "+1 ###-###-####", TV: ["+688 ## ###", "+688 ## ####"], TW: ["+886 # #### ####", "+886 ### ### ###", "+886 ## ### ####", "+886 ## #### ####"], TZ: ["+255 ## ### ####", "+255 ### ### ###", "+255 ### ## ####"], UA: ["+380 #### #####", "+380 ## ### ####", "+380 ### ### ###"], UG: ["+256 ## #######", "+256 ### ######"], US: "+1 ###-###-####", UY: ["+598 #### ####", "+598 ## ### ###", "+598 ### ####"], UZ: "+998 ## ### ## ##", VA: ["+39 ## #### ####", "+39 ### ### ####", "+39 ### ### ###"], VC: "+1 ###-###-####", VE: "+58 ###-#######", VG: "+1 ###-###-####", VI: "+1 ###-###-####", VN: ["+84 ### #### ###", "+84 ### ### ###", "+84 #### ######", "+84 ## ### ## ##"], VU: ["+678 #####", "+678 ### ####"], WF: ["+681 ## ## ##", "+681 ### ## ## ##"], WS: ["+685 #####", "+685 ## #####", "+685 ### ###"], XK: ["+383 ## ### ###", "+383 ### #####"], YE: ["+967 # ### ###", "+967 ### ### ###"], YT: "+262 ### ## ## ##", ZA: ["+27 ## ### ####", "+27 ### ### ###"], ZM: ["+260 ### ### ###", "+260 ## #######", "+260 #########"], ZW: ["+263 ## #####", "+263 ## ### ####", "+263 ### ####", "+263 #### ######"] };
4
+ const t$1 = /^[a-z]{2}$/i, countryCodeEmoji = (o) => {
5
+ if (!t$1.test(o)) {
6
+ const t2 = typeof o;
7
+ throw new TypeError(`cc argument must be an ISO 3166-1 alpha-2 string, but got '${"string" === t2 ? o : t2}' instead.`);
8
+ }
9
+ const e = [...o.toUpperCase()].map((t2) => (t2.codePointAt(0) ?? 0) + 127397);
10
+ return String.fromCodePoint(...e);
11
+ };
12
+ const t = Object.entries(M), divideMask = (e) => e.split(/ (.*)/s);
13
+ function getCodeAndMask(e) {
14
+ let n = "", t2 = "";
15
+ if (Array.isArray(e)) {
16
+ const o = [];
17
+ for (const t3 of e) {
18
+ const [e2, s] = divideMask(t3);
19
+ n || (n = e2), o.push(s);
20
+ }
21
+ t2 = o;
22
+ } else {
23
+ const [o, s] = divideMask(e);
24
+ n = o, t2 = s;
25
+ }
26
+ return [n, t2];
27
+ }
28
+ t.map(([e, n]) => ({ id: e, mask: n }));
29
+ t.reduce((e, [n, t2]) => {
30
+ const [o, s] = getCodeAndMask(t2);
31
+ return e[n] = { code: o, mask: s }, e;
32
+ }, {});
33
+ t.map(([e, n]) => {
34
+ const [t2, o] = getCodeAndMask(n);
35
+ return { id: e, code: t2, mask: o };
36
+ });
37
+ t.reduce((e, [t2, o]) => {
38
+ const [s, a] = getCodeAndMask(o);
39
+ return e[t2] = { code: s, mask: a, flag: countryCodeEmoji(t2) }, e;
40
+ }, {});
41
+ t.map(([e, t2]) => {
42
+ const [o, s] = getCodeAndMask(t2);
43
+ return { id: e, code: o, mask: s, flag: countryCodeEmoji(e) };
44
+ });
45
+ const MasksFullMap = (e) => {
46
+ const o = new Intl.DisplayNames([e], { type: "region" });
47
+ return t.reduce((e2, [t2, s]) => {
48
+ const [a, r] = getCodeAndMask(s), d = o.of(t2) ?? "";
49
+ return e2[t2] = { code: a, mask: r, name: d, flag: countryCodeEmoji(t2) }, e2;
50
+ }, {});
51
+ }, MasksFull = (e) => {
52
+ const o = new Intl.DisplayNames([e], { type: "region" });
53
+ return t.map(([e2, t2]) => {
54
+ const [s, a] = getCodeAndMask(t2);
55
+ return { id: e2, code: s, mask: a, name: o.of(e2) ?? "", flag: countryCodeEmoji(e2) };
56
+ });
57
+ }, m = t.reduce((e, [t2, o]) => {
58
+ const [s, a] = getCodeAndMask(o), r = new Intl.DisplayNames(["en"], { type: "region" });
59
+ return e[t2] = { code: s, mask: a, name: r.of(t2) ?? "", flag: countryCodeEmoji(t2) }, e;
60
+ }, {}), i = t.map(([e, t2]) => {
61
+ const [o, s] = getCodeAndMask(t2);
62
+ return { id: e, code: o, mask: s, name: new Intl.DisplayNames(["en"], { type: "region" }).of(e) ?? "", flag: countryCodeEmoji(e) };
63
+ }), g = countryCodeEmoji;
64
+ function toArray(t2) {
65
+ return Array.isArray(t2) ? t2 : [t2];
66
+ }
67
+ function countPlaceholders(t2) {
68
+ return (t2.match(/#/g) || []).length;
69
+ }
70
+ function removeCountryCodePrefix(t2) {
71
+ return t2.replace(/^\+\d+\s?/, "");
72
+ }
73
+ function pickMaskVariant(t2, n) {
74
+ if (1 === t2.length) return t2[0];
75
+ const r = t2.map((t3) => ({ mask: t3, count: countPlaceholders(t3) })), e = r.filter((t3) => t3.count >= n).sort((t3, n2) => t3.count - n2.count);
76
+ if (e.length > 0) return e[0].mask;
77
+ const o = r.sort((t3, n2) => n2.count - t3.count)[0];
78
+ return o ? o.mask : t2[0];
79
+ }
80
+ function formatDigitsWithMap(t2, n) {
81
+ let r = "";
82
+ const e = [];
83
+ let o = 0;
84
+ const c = n.length, a = t2.length;
85
+ for (let i2 = 0; i2 < a; i2++) {
86
+ const a2 = t2[i2];
87
+ if ("#" === a2) {
88
+ if (!(o < c)) break;
89
+ r += n[o], e.push(o), o++;
90
+ } else {
91
+ const n2 = -1 !== t2.indexOf("#", i2 + 1) && o < c;
92
+ (r.length > 0 || n2) && (r += a2, e.push(-1));
93
+ }
94
+ }
95
+ return { display: r, map: e };
96
+ }
97
+ const CACHE_KEY = "@desource/phone-mask:geo";
98
+ const CACHE_EXPIRY_MS = 24 * 60 * 6e4;
99
+ const GEO_IP_TIMEOUT = 1500;
100
+ const GEO_IP_URL = "https://ipapi.co/json/";
101
+ const Delimiters = [" ", "-", "(", ")"];
102
+ const NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "Tab"];
103
+ const InvalidPattern = /[^\d\s\-()]/;
104
+ const emptyCountry = { id: "", code: "", mask: "", flag: "", name: "" };
105
+ function useCountrySelector(usedLocale) {
106
+ const isEnLocale = vue.computed(() => usedLocale.value.toLowerCase().startsWith("en"));
107
+ const countries = vue.computed(() => isEnLocale.value ? i : MasksFull(usedLocale.value));
108
+ const countriesMap = vue.computed(() => isEnLocale.value ? m : MasksFullMap(usedLocale.value));
109
+ const selectedId = vue.ref(countries.value[0]?.id || "");
110
+ const dropdownOpened = vue.ref(false);
111
+ const hasDropdown = vue.ref(true);
112
+ const search = vue.ref("");
113
+ const focusedIndex = vue.ref(0);
114
+ const selected = vue.computed(() => {
115
+ const id = selectedId.value;
116
+ const found = countriesMap.value[id];
117
+ return found ? { id, ...found } : countries.value[0] || emptyCountry;
118
+ });
119
+ const hasCountry = (id) => {
120
+ const _id = id.toUpperCase();
121
+ return !!countriesMap.value[_id];
122
+ };
123
+ const filteredCountries = vue.computed(() => {
124
+ const q = search.value.trim().toUpperCase();
125
+ if (!q) return countries.value;
126
+ const qCodeDigits = q.replace(/\D/g, "");
127
+ const isNumericSearch = qCodeDigits.length > 0;
128
+ return countries.value.map((c) => {
129
+ const nameUpper = c.name.toUpperCase();
130
+ const idUpper = c.id.toUpperCase();
131
+ const codeDigits = c.code.replace(/\D/g, "");
132
+ let score = 0;
133
+ if (nameUpper.startsWith(q)) score = 1e3;
134
+ else if (nameUpper.includes(q)) score = 500;
135
+ if (c.code.startsWith(q)) score += 100;
136
+ else if (c.code.includes(q)) score += 50;
137
+ if (idUpper === q) score += 200;
138
+ else if (idUpper.startsWith(q)) score += 150;
139
+ if (isNumericSearch && codeDigits.startsWith(qCodeDigits)) score += 80;
140
+ else if (isNumericSearch && codeDigits.includes(qCodeDigits)) score += 40;
141
+ return { country: c, score };
142
+ }).filter(({ score }) => score > 0).sort((a, b) => {
143
+ if (b.score !== a.score) return b.score - a.score;
144
+ return a.country.name.localeCompare(b.country.name);
145
+ }).map(({ country }) => country);
146
+ });
147
+ const selectCountry = (id) => {
148
+ selectedId.value = id;
149
+ closeDropdown();
150
+ };
151
+ const toggleDropdown = async (searchRef) => {
152
+ dropdownOpened.value = !dropdownOpened.value;
153
+ if (!dropdownOpened.value) return;
154
+ await vue.nextTick();
155
+ searchRef.value?.focus();
156
+ focusedIndex.value = 0;
157
+ };
158
+ const closeDropdown = () => {
159
+ dropdownOpened.value = false;
160
+ };
161
+ const focusNextOption = (scrollFn) => {
162
+ if (filteredCountries.value.length === 0) return;
163
+ focusedIndex.value = Math.min(filteredCountries.value.length - 1, focusedIndex.value + 1);
164
+ scrollFn?.();
165
+ };
166
+ const focusPrevOption = (scrollFn) => {
167
+ if (filteredCountries.value.length === 0) return;
168
+ focusedIndex.value = Math.max(0, focusedIndex.value - 1);
169
+ scrollFn?.();
170
+ };
171
+ const chooseFocusedOption = () => {
172
+ const item = filteredCountries.value[focusedIndex.value];
173
+ if (item) selectCountry(item.id);
174
+ };
175
+ const detectFromLocale = () => {
176
+ try {
177
+ const lang = navigator.language || "";
178
+ try {
179
+ const loc = new Intl.Locale(lang);
180
+ if (loc.region && hasCountry(loc.region)) return loc.region.toUpperCase();
181
+ } catch {
182
+ }
183
+ const parts = lang.split(/[-_]/);
184
+ if (parts.length > 1 && hasCountry(parts[1])) return parts[1].toUpperCase();
185
+ } catch {
186
+ }
187
+ return null;
188
+ };
189
+ const detectByGeoIp = async () => {
190
+ try {
191
+ const cached = localStorage.getItem(CACHE_KEY);
192
+ if (cached) {
193
+ const parsed = JSON.parse(cached);
194
+ const isExpired = Date.now() - parsed.ts > CACHE_EXPIRY_MS;
195
+ if (!isExpired && parsed.country_code && hasCountry(parsed.country_code)) {
196
+ return parsed.country_code.toUpperCase();
197
+ }
198
+ if (isExpired) {
199
+ localStorage.removeItem(CACHE_KEY);
200
+ }
201
+ }
202
+ } catch {
203
+ }
204
+ const controller = new AbortController();
205
+ const timeoutId = setTimeout(() => controller.abort(), GEO_IP_TIMEOUT);
206
+ try {
207
+ const res = await fetch(GEO_IP_URL, {
208
+ signal: controller.signal,
209
+ headers: { Accept: "application/json" }
210
+ });
211
+ if (!res.ok) {
212
+ return null;
213
+ }
214
+ const json = await res.json();
215
+ const code = (json.country || json.country_code || json.countryCode || json.country_code2 || "").toString().toUpperCase();
216
+ if (code && hasCountry(code)) {
217
+ try {
218
+ localStorage.setItem(CACHE_KEY, JSON.stringify({ country_code: code, ts: Date.now() }));
219
+ } catch {
220
+ }
221
+ return code;
222
+ }
223
+ } catch {
224
+ } finally {
225
+ clearTimeout(timeoutId);
226
+ }
227
+ return null;
228
+ };
229
+ const selectInitialCountry = (id, emitFn) => {
230
+ const previousId = selectedId.value;
231
+ selectedId.value = id;
232
+ if (previousId !== selectedId.value && emitFn) vue.nextTick(emitFn);
233
+ };
234
+ const initCountry = async (predefined, detect, emitFn) => {
235
+ hasDropdown.value = !predefined && countries.value.length > 1;
236
+ if (predefined && hasCountry(predefined)) {
237
+ selectInitialCountry(predefined.toUpperCase(), emitFn);
238
+ return;
239
+ }
240
+ if (!detect) return;
241
+ const geo = await detectByGeoIp();
242
+ if (geo) {
243
+ selectInitialCountry(geo, emitFn);
244
+ return;
245
+ }
246
+ const loc = detectFromLocale();
247
+ if (loc) {
248
+ selectInitialCountry(loc, emitFn);
249
+ return;
250
+ }
251
+ };
252
+ return {
253
+ countries,
254
+ selectedId,
255
+ selected,
256
+ hasCountry,
257
+ // Dropdown
258
+ hasDropdown,
259
+ dropdownOpened,
260
+ search,
261
+ focusedIndex,
262
+ filteredCountries,
263
+ selectCountry,
264
+ toggleDropdown,
265
+ closeDropdown,
266
+ focusNextOption,
267
+ focusPrevOption,
268
+ chooseFocusedOption,
269
+ // Country Detection
270
+ initCountry
271
+ };
272
+ }
273
+ function createPhoneFormatter(country) {
274
+ const variants = toArray(country.mask);
275
+ const variantsDigits = variants.map((m2) => countPlaceholders(removeCountryCodePrefix(m2)));
276
+ const maxDigits = Math.max(...variantsDigits);
277
+ const getMask = (digitLength) => {
278
+ const mask = pickMaskVariant(variants, digitLength);
279
+ return removeCountryCodePrefix(mask);
280
+ };
281
+ return {
282
+ formatDisplay: (digits) => {
283
+ const template = getMask(digits.length);
284
+ return formatDigitsWithMap(template, digits).display;
285
+ },
286
+ getMaxDigits: () => maxDigits,
287
+ getPlaceholder: () => {
288
+ const template = getMask(0);
289
+ return template;
290
+ },
291
+ getCaretPosition: (digitIndex) => {
292
+ const template = getMask(digitIndex);
293
+ const { display, map } = formatDigitsWithMap(template, "0".repeat(digitIndex));
294
+ for (let i2 = 0; i2 < map.length; i2++) {
295
+ if (map[i2] === digitIndex) return i2;
296
+ }
297
+ if (digitIndex >= map.length) return display.length;
298
+ for (let i2 = 0; i2 < map.length; i2++) {
299
+ if (map[i2] > digitIndex) return i2;
300
+ }
301
+ return display.length;
302
+ },
303
+ getDigitRange: (digits, selStart, selEnd) => {
304
+ const template = getMask(digits.length);
305
+ const { map } = formatDigitsWithMap(template, digits);
306
+ let min = Infinity;
307
+ let max = -Infinity;
308
+ for (let i2 = selStart; i2 < selEnd && i2 < map.length; i2++) {
309
+ const digitIdx = map[i2];
310
+ if (digitIdx !== void 0 && digitIdx >= 0) {
311
+ min = Math.min(min, digitIdx);
312
+ max = Math.max(max, digitIdx);
313
+ }
314
+ }
315
+ return min === Infinity ? null : [min, max + 1];
316
+ },
317
+ isComplete: (digits) => {
318
+ return variantsDigits.includes(digits.length);
319
+ }
320
+ };
321
+ }
322
+ function setCaret(el, position) {
323
+ if (!el) return;
324
+ try {
325
+ el.setSelectionRange(position, position);
326
+ } catch {
327
+ }
328
+ }
329
+ function extractDigits(value, maxLength) {
330
+ const digits = value.replace(/\D/g, "");
331
+ return maxLength ? digits.slice(0, maxLength) : digits;
332
+ }
333
+ function getSelection(el) {
334
+ if (!el) return [0, 0];
335
+ return [el.selectionStart ?? 0, el.selectionEnd ?? 0];
336
+ }
337
+ function useMask(selected, telRef) {
338
+ const digits = vue.ref("");
339
+ const displayValue = vue.ref("");
340
+ const validationTimer = vue.ref(null);
341
+ const showValidationHint = vue.ref(false);
342
+ const formatter = vue.computed(() => createPhoneFormatter(selected.value));
343
+ const displayPlaceholder = vue.computed(() => formatter.value.getPlaceholder());
344
+ const isComplete = vue.computed(() => formatter.value.isComplete(digits.value));
345
+ const isEmpty = vue.computed(() => digits.value.length === 0);
346
+ const maxDigits = vue.computed(() => formatter.value.getMaxDigits());
347
+ const shouldShowWarn = vue.computed(() => showValidationHint.value && !isEmpty.value && !isComplete.value);
348
+ const fullFormatted = vue.computed(() => {
349
+ if (!displayValue.value) return "";
350
+ return `${selected.value.code} ${displayValue.value}`;
351
+ });
352
+ const full = vue.computed(() => {
353
+ if (!digits.value) return "";
354
+ return `${selected.value.code}${digits.value}`;
355
+ });
356
+ const updateDisplay2 = () => {
357
+ displayValue.value = formatter.value.formatDisplay(digits.value);
358
+ };
359
+ const setCaretToDigitPosition = (digitIndex) => {
360
+ const pos = formatter.value.getCaretPosition(digitIndex);
361
+ setCaret(telRef.value, pos);
362
+ };
363
+ const removeDigitsRange = (startIdx, endIdx) => {
364
+ if (startIdx >= endIdx) return;
365
+ digits.value = digits.value.slice(0, startIdx) + digits.value.slice(endIdx);
366
+ };
367
+ const handleBeforeInput = (e) => {
368
+ const el = e.target;
369
+ if (!el) return;
370
+ const data = e.data;
371
+ if (e.inputType !== "insertText" || !data) return;
372
+ if (InvalidPattern.test(data) || data === " " && el.value.endsWith(" ")) {
373
+ e.preventDefault();
374
+ }
375
+ };
376
+ const handleInput = (e) => {
377
+ const el = e.target;
378
+ if (!el) return;
379
+ const newDigits = extractDigits(el.value, maxDigits.value);
380
+ showValidationHint.value = false;
381
+ if (validationTimer.value) {
382
+ clearTimeout(validationTimer.value);
383
+ }
384
+ digits.value = newDigits;
385
+ updateDisplay2();
386
+ if (newDigits.length > 0) {
387
+ validationTimer.value = setTimeout(() => {
388
+ showValidationHint.value = true;
389
+ }, 500);
390
+ }
391
+ vue.nextTick(() => {
392
+ setCaretToDigitPosition(digits.value.length);
393
+ });
394
+ };
395
+ const handleKeydownInternal = (e) => {
396
+ const el = telRef.value ?? e.target;
397
+ if (!el) return;
398
+ if (e.ctrlKey || e.metaKey || e.altKey || NavigationKeys.includes(e.key)) return;
399
+ const [selStart, selEnd] = getSelection(el);
400
+ if (e.key === "Backspace") {
401
+ e.preventDefault();
402
+ if (selStart !== selEnd) {
403
+ const range = formatter.value.getDigitRange(digits.value, selStart, selEnd);
404
+ if (range) {
405
+ const [start, end] = range;
406
+ removeDigitsRange(start, end);
407
+ updateDisplay2();
408
+ vue.nextTick(() => setCaretToDigitPosition(start));
409
+ }
410
+ return;
411
+ }
412
+ if (selStart > 0) {
413
+ const displayStr = displayValue.value;
414
+ let prevPos = selStart - 1;
415
+ while (prevPos >= 0 && Delimiters.includes(displayStr[prevPos])) {
416
+ prevPos--;
417
+ }
418
+ if (prevPos >= 0) {
419
+ const range = formatter.value.getDigitRange(digits.value, prevPos, prevPos + 1);
420
+ if (range) {
421
+ const [start] = range;
422
+ removeDigitsRange(start, start + 1);
423
+ updateDisplay2();
424
+ vue.nextTick(() => setCaretToDigitPosition(start));
425
+ }
426
+ }
427
+ }
428
+ return;
429
+ }
430
+ if (e.key === "Delete") {
431
+ e.preventDefault();
432
+ if (selStart !== selEnd) {
433
+ const range = formatter.value.getDigitRange(digits.value, selStart, selEnd);
434
+ if (range) {
435
+ const [start, end] = range;
436
+ removeDigitsRange(start, end);
437
+ updateDisplay2();
438
+ vue.nextTick(() => setCaretToDigitPosition(start));
439
+ }
440
+ return;
441
+ }
442
+ if (selStart < displayValue.value.length) {
443
+ const range = formatter.value.getDigitRange(digits.value, selStart, selStart + 1);
444
+ if (range) {
445
+ const [start] = range;
446
+ removeDigitsRange(start, start + 1);
447
+ updateDisplay2();
448
+ vue.nextTick(() => setCaretToDigitPosition(start));
449
+ }
450
+ }
451
+ return;
452
+ }
453
+ if (/^[0-9]$/.test(e.key)) {
454
+ if (digits.value.length >= maxDigits.value) {
455
+ e.preventDefault();
456
+ }
457
+ return;
458
+ }
459
+ if (e.key.length === 1) {
460
+ e.preventDefault();
461
+ }
462
+ };
463
+ const handleKeydown = (e) => {
464
+ showValidationHint.value = false;
465
+ if (validationTimer.value) {
466
+ clearTimeout(validationTimer.value);
467
+ }
468
+ handleKeydownInternal(e);
469
+ if (validationTimer.value) clearTimeout(validationTimer.value);
470
+ validationTimer.value = setTimeout(() => {
471
+ if (!isComplete.value && !isEmpty.value) showValidationHint.value = true;
472
+ }, 300);
473
+ };
474
+ const handlePaste = (e) => {
475
+ e.preventDefault();
476
+ const text = e.clipboardData?.getData("text") || "";
477
+ const pastedDigits = extractDigits(text, maxDigits.value);
478
+ if (pastedDigits.length === 0) return;
479
+ const el = telRef.value;
480
+ if (!el) return;
481
+ const [selStart, selEnd] = getSelection(el);
482
+ if (selStart !== selEnd) {
483
+ const range2 = formatter.value.getDigitRange(digits.value, selStart, selEnd);
484
+ if (range2) {
485
+ const [start, end] = range2;
486
+ const left2 = digits.value.slice(0, start);
487
+ const right2 = digits.value.slice(end);
488
+ digits.value = extractDigits(left2 + pastedDigits + right2, maxDigits.value);
489
+ updateDisplay2();
490
+ vue.nextTick(() => setCaretToDigitPosition(start + pastedDigits.length));
491
+ return;
492
+ }
493
+ }
494
+ const range = formatter.value.getDigitRange(digits.value, selStart, selStart);
495
+ const insertIndex = range ? range[0] : digits.value.length;
496
+ const left = digits.value.slice(0, insertIndex);
497
+ const right = digits.value.slice(insertIndex);
498
+ digits.value = extractDigits(left + pastedDigits + right, maxDigits.value);
499
+ updateDisplay2();
500
+ if (validationTimer.value) clearTimeout(validationTimer.value);
501
+ validationTimer.value = setTimeout(() => {
502
+ if (!isComplete.value && !isEmpty.value) showValidationHint.value = true;
503
+ }, 300);
504
+ vue.nextTick(() => setCaretToDigitPosition(insertIndex + pastedDigits.length));
505
+ };
506
+ const handleFocus = () => {
507
+ if (validationTimer.value) {
508
+ clearTimeout(validationTimer.value);
509
+ }
510
+ };
511
+ const clear = () => {
512
+ digits.value = "";
513
+ displayValue.value = "";
514
+ showValidationHint.value = false;
515
+ if (validationTimer.value) {
516
+ clearTimeout(validationTimer.value);
517
+ validationTimer.value = null;
518
+ }
519
+ };
520
+ vue.watch(selected, () => {
521
+ if (digits.value.length > maxDigits.value) {
522
+ digits.value = digits.value.slice(0, maxDigits.value);
523
+ }
524
+ updateDisplay2();
525
+ });
526
+ updateDisplay2();
527
+ return {
528
+ // State
529
+ digits,
530
+ displayValue,
531
+ // Computed
532
+ displayPlaceholder,
533
+ isComplete,
534
+ isEmpty,
535
+ shouldShowWarn,
536
+ fullFormatted,
537
+ full,
538
+ // Handlers
539
+ handleBeforeInput,
540
+ handleInput,
541
+ handleKeydown,
542
+ handlePaste,
543
+ handleFocus,
544
+ // Methods
545
+ updateDisplayFromDigits: updateDisplay2,
546
+ clear
547
+ };
548
+ }
549
+ function useClipboard() {
550
+ const copied = vue.ref(false);
551
+ const isCopying = vue.ref(false);
552
+ let copyTimer = null;
553
+ const clearTimer = () => {
554
+ if (copyTimer) {
555
+ clearTimeout(copyTimer);
556
+ copyTimer = null;
557
+ }
558
+ };
559
+ const copy = async (text) => {
560
+ if (isCopying.value) return false;
561
+ const trimmedText = text.trim();
562
+ if (!trimmedText) return false;
563
+ isCopying.value = true;
564
+ try {
565
+ await navigator.clipboard.writeText(trimmedText);
566
+ copied.value = true;
567
+ clearTimer();
568
+ copyTimer = setTimeout(() => {
569
+ copied.value = false;
570
+ copyTimer = null;
571
+ }, 1800);
572
+ return true;
573
+ } catch (err) {
574
+ console.warn("Copy failed", err);
575
+ return false;
576
+ } finally {
577
+ isCopying.value = false;
578
+ }
579
+ };
580
+ const onUnmount = () => {
581
+ clearTimer();
582
+ };
583
+ return { copied, isCopying, copy, onUnmount };
584
+ }
585
+ const _hoisted_1 = { class: "pi-selector" };
586
+ const _hoisted_2 = ["disabled", "tabindex", "aria-label", "aria-expanded", "aria-haspopup"];
587
+ const _hoisted_3 = ["aria-label"];
588
+ const _hoisted_4 = { class: "pi-code" };
589
+ const _hoisted_5 = { class: "pi-input-wrap" };
590
+ const _hoisted_6 = ["placeholder", "value", "disabled", "readonly", "aria-invalid"];
591
+ const _hoisted_7 = {
592
+ class: "pi-actions",
593
+ role: "toolbar",
594
+ "aria-label": "Phone input actions"
595
+ };
596
+ const _hoisted_8 = ["aria-label", "title"];
597
+ const _hoisted_9 = {
598
+ key: 1,
599
+ width: "16",
600
+ height: "16",
601
+ viewBox: "0 0 16 16",
602
+ fill: "none",
603
+ "aria-hidden": "true"
604
+ };
605
+ const _hoisted_10 = {
606
+ key: 2,
607
+ width: "16",
608
+ height: "16",
609
+ viewBox: "0 0 16 16",
610
+ fill: "none",
611
+ "aria-hidden": "true"
612
+ };
613
+ const _hoisted_11 = ["aria-label", "title"];
614
+ const _hoisted_12 = {
615
+ key: 0,
616
+ width: "11",
617
+ height: "11",
618
+ viewBox: "0 0 14 14",
619
+ fill: "none",
620
+ "aria-hidden": "true"
621
+ };
622
+ const _hoisted_13 = { class: "pi-search-wrap" };
623
+ const _hoisted_14 = ["placeholder"];
624
+ const _hoisted_15 = ["aria-activedescendant"];
625
+ const _hoisted_16 = ["id", "aria-selected", "title", "onClick", "onMouseenter"];
626
+ const _hoisted_17 = ["aria-label"];
627
+ const _hoisted_18 = { class: "pi-opt-name" };
628
+ const _hoisted_19 = { class: "pi-opt-code" };
629
+ const _hoisted_20 = {
630
+ key: 0,
631
+ class: "pi-empty"
632
+ };
633
+ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
634
+ __name: "PhoneInput",
635
+ props: /* @__PURE__ */ vue.mergeModels({
636
+ country: {},
637
+ detect: { type: Boolean, default: true },
638
+ locale: {},
639
+ size: { default: "normal" },
640
+ theme: { default: "auto" },
641
+ disabled: { type: Boolean, default: false },
642
+ readonly: { type: Boolean, default: false },
643
+ showCopy: { type: Boolean, default: true },
644
+ showClear: { type: Boolean, default: false },
645
+ withValidity: { type: Boolean, default: true },
646
+ searchPlaceholder: { default: "Search country or code..." },
647
+ noResultsText: { default: "No countries found" },
648
+ clearButtonLabel: { default: "Clear phone number" },
649
+ dropdownClass: {},
650
+ disableDefaultStyles: { type: Boolean, default: false }
651
+ }, {
652
+ "modelValue": {},
653
+ "modelModifiers": {}
654
+ }),
655
+ emits: /* @__PURE__ */ vue.mergeModels(["change", "country-change", "validation-change", "focus", "blur", "copy", "clear"], ["update:modelValue"]),
656
+ setup(__props, { expose: __expose, emit: __emit }) {
657
+ const props = __props;
658
+ const slots = vue.useSlots();
659
+ const model = vue.useModel(__props, "modelValue");
660
+ const emit = __emit;
661
+ const rootRef = vue.useTemplateRef("rootRef");
662
+ const telRef = vue.useTemplateRef("telRef");
663
+ const searchRef = vue.useTemplateRef("searchRef");
664
+ const liveRef = vue.useTemplateRef("liveRef");
665
+ const dropdownRef = vue.useTemplateRef("dropdownRef");
666
+ const usedLocale = vue.computed(() => {
667
+ if (props.locale) return props.locale;
668
+ if (typeof navigator !== "undefined") {
669
+ return navigator.language || navigator.userLanguage || "en";
670
+ }
671
+ return "en";
672
+ });
673
+ const dropdownStyle = vue.shallowRef({});
674
+ const countrySelector = useCountrySelector(usedLocale);
675
+ const {
676
+ search,
677
+ filteredCountries,
678
+ focusedIndex,
679
+ selected,
680
+ dropdownOpened,
681
+ hasDropdown,
682
+ focusNextOption,
683
+ focusPrevOption,
684
+ chooseFocusedOption,
685
+ closeDropdown
686
+ } = countrySelector;
687
+ const mask = useMask(selected, telRef);
688
+ const { digits, displayValue, displayPlaceholder, isComplete, isEmpty, shouldShowWarn, full, fullFormatted } = mask;
689
+ const { copied, copy, onUnmount: onClipboardUnmount } = useClipboard();
690
+ const inactive = vue.computed(() => props.disabled || props.readonly);
691
+ const showCopyButton = vue.computed(() => props.showCopy && !isEmpty.value && !props.disabled);
692
+ const showClearButton = vue.computed(() => props.showClear && !isEmpty.value && !inactive.value);
693
+ const copyAriaLabel = vue.computed(() => copied.value ? "Copied" : `Copy ${selected.value.code} ${displayValue.value}`);
694
+ const copyButtonTitle = vue.computed(() => copied.value ? "Copied" : "Copy phone number");
695
+ const copyMessage = vue.computed(() => copied.value ? "Phone number copied to clipboard" : "");
696
+ const sizeClass = vue.computed(() => `size-${props.size}`);
697
+ const themeClass = vue.computed(() => {
698
+ if (props.theme !== "auto") return `theme-${props.theme}`;
699
+ if (typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
700
+ return "theme-dark";
701
+ }
702
+ return "theme-light";
703
+ });
704
+ const rootClasses = vue.computed(() => [
705
+ "phone-input",
706
+ sizeClass.value,
707
+ themeClass.value,
708
+ {
709
+ "is-disabled": props.disabled,
710
+ "is-readonly": props.readonly,
711
+ "is-unstyled": props.disableDefaultStyles,
712
+ "is-incomplete": props.withValidity && shouldShowWarn.value,
713
+ "is-complete": props.withValidity && isComplete.value
714
+ }
715
+ ]);
716
+ const rootStyles = vue.computed(() => ({
717
+ "--pi-actions-count": +showCopyButton.value + +showClearButton.value + (slots["actions-before"] ? 1 : 0)
718
+ }));
719
+ const emitModelUpdate = () => {
720
+ if (model.value === digits.value) return;
721
+ model.value = digits.value;
722
+ emit("change", {
723
+ full: full.value,
724
+ fullFormatted: fullFormatted.value,
725
+ digits: digits.value
726
+ });
727
+ };
728
+ const onInput = async (e) => {
729
+ if (inactive.value) return;
730
+ mask.handleInput(e);
731
+ await vue.nextTick();
732
+ emitModelUpdate();
733
+ };
734
+ const onKeydown = async (e) => {
735
+ if (inactive.value) return;
736
+ mask.handleKeydown(e);
737
+ await vue.nextTick();
738
+ emitModelUpdate();
739
+ };
740
+ const onPaste = async (e) => {
741
+ if (inactive.value) return;
742
+ mask.handlePaste(e);
743
+ await vue.nextTick();
744
+ emitModelUpdate();
745
+ };
746
+ const onFocus = (e) => {
747
+ mask.handleFocus();
748
+ dropdownOpened.value = false;
749
+ emit("focus", e);
750
+ };
751
+ const onBlur = (e) => emit("blur", e);
752
+ const onSelectCountry = async (countryId) => {
753
+ countrySelector.selectCountry(countryId);
754
+ emit("country-change", selected.value);
755
+ await vue.nextTick();
756
+ telRef.value?.focus();
757
+ };
758
+ const onCopyClick = async () => {
759
+ const valueToCopy = fullFormatted.value;
760
+ const success = await copy(valueToCopy);
761
+ if (success) {
762
+ emit("copy", valueToCopy);
763
+ }
764
+ };
765
+ const onClearClick = async () => {
766
+ mask.clear();
767
+ model.value = "";
768
+ emit("change", {
769
+ full: "",
770
+ fullFormatted: "",
771
+ digits: ""
772
+ });
773
+ emit("clear");
774
+ await vue.nextTick();
775
+ telRef.value?.focus();
776
+ };
777
+ const positionDropdown = (e) => {
778
+ if (e?.type === "scroll" && e.target && dropdownRef.value?.contains(e.target)) return;
779
+ const root = rootRef.value;
780
+ if (!root) return;
781
+ const rect = root.getBoundingClientRect();
782
+ dropdownStyle.value = {
783
+ top: `${rect.bottom + window.scrollY + 8}px`,
784
+ left: `${rect.left + window.scrollX}px`,
785
+ width: `${rect.width}px`
786
+ };
787
+ };
788
+ const removeDropdownListeners = () => {
789
+ window.removeEventListener("scroll", positionDropdown, true);
790
+ window.removeEventListener("click", onDocClick, true);
791
+ window.removeEventListener("resize", positionDropdown);
792
+ };
793
+ const toggleDropdown = async () => {
794
+ if (inactive.value || !hasDropdown.value) return;
795
+ await countrySelector.toggleDropdown(searchRef);
796
+ if (dropdownOpened.value) {
797
+ positionDropdown();
798
+ window.addEventListener("scroll", positionDropdown, true);
799
+ window.addEventListener("click", onDocClick, true);
800
+ window.addEventListener("resize", positionDropdown);
801
+ } else {
802
+ removeDropdownListeners();
803
+ }
804
+ };
805
+ const scrollFocusedIntoView = async () => {
806
+ await vue.nextTick();
807
+ const list = dropdownRef.value?.lastElementChild;
808
+ if (!list) return;
809
+ list.children[focusedIndex.value]?.scrollIntoView?.({ block: "nearest" });
810
+ };
811
+ const onDocClick = (ev) => {
812
+ const dropdown = dropdownRef.value;
813
+ const selector = rootRef.value?.firstChild;
814
+ if (!(dropdown || selector)) return;
815
+ const target = ev.target;
816
+ if (!target || dropdown?.contains(target) || selector?.contains(target)) return;
817
+ dropdownOpened.value = false;
818
+ };
819
+ vue.watch(
820
+ model,
821
+ (newValue) => {
822
+ if (!newValue) {
823
+ if (!isEmpty.value) mask.clear();
824
+ return;
825
+ }
826
+ const currentDigits = digits.value;
827
+ if (newValue !== currentDigits) {
828
+ const incomingDigits = newValue.replace(/\D/g, "");
829
+ if (incomingDigits !== currentDigits) {
830
+ digits.value = incomingDigits;
831
+ mask.updateDisplayFromDigits();
832
+ }
833
+ }
834
+ },
835
+ { immediate: true }
836
+ );
837
+ vue.watch(
838
+ [() => props.country, () => props.detect],
839
+ async ([country, detect]) => {
840
+ await vue.nextTick();
841
+ await countrySelector.initCountry(country, detect, () => emit("country-change", selected.value));
842
+ },
843
+ { immediate: true }
844
+ );
845
+ vue.watch(
846
+ copyMessage,
847
+ (val) => {
848
+ if (liveRef.value && val) {
849
+ liveRef.value.textContent = val;
850
+ }
851
+ },
852
+ { flush: "post" }
853
+ );
854
+ vue.watch(
855
+ isComplete,
856
+ (valid) => {
857
+ emit("validation-change", valid);
858
+ },
859
+ { flush: "post" }
860
+ );
861
+ vue.onBeforeUnmount(() => {
862
+ removeDropdownListeners();
863
+ onClipboardUnmount();
864
+ });
865
+ __expose({
866
+ focus: () => telRef.value?.focus(),
867
+ blur: () => telRef.value?.blur(),
868
+ clear: mask.clear,
869
+ selectCountry: countrySelector.selectCountry,
870
+ getFullNumber: () => full.value,
871
+ getFullFormattedNumber: () => fullFormatted.value,
872
+ getDigits: () => digits.value,
873
+ isValid: () => isComplete.value,
874
+ isComplete: () => isComplete.value
875
+ });
876
+ return (_ctx, _cache) => {
877
+ return vue.openBlock(), vue.createElementBlock("div", {
878
+ ref_key: "rootRef",
879
+ ref: rootRef,
880
+ "aria-label": "Phone input with country selector",
881
+ role: "group",
882
+ class: vue.normalizeClass(rootClasses.value),
883
+ style: vue.normalizeStyle(rootStyles.value)
884
+ }, [
885
+ vue.createElementVNode("div", _hoisted_1, [
886
+ vue.createElementVNode("button", {
887
+ type: "button",
888
+ class: vue.normalizeClass(["pi-selector-btn", { "no-dropdown": !vue.unref(hasDropdown) || __props.readonly }]),
889
+ disabled: __props.disabled,
890
+ tabindex: inactive.value || !vue.unref(hasDropdown) ? -1 : void 0,
891
+ "aria-label": `Selected country: ${vue.unref(selected).name}`,
892
+ "aria-expanded": vue.unref(dropdownOpened),
893
+ "aria-haspopup": vue.unref(hasDropdown) ? "listbox" : void 0,
894
+ onClick: toggleDropdown
895
+ }, [
896
+ vue.createElementVNode("span", {
897
+ class: "pi-flag",
898
+ role: "img",
899
+ "aria-label": `${vue.unref(selected).name} flag`
900
+ }, [
901
+ vue.renderSlot(_ctx.$slots, "flag", { country: vue.unref(selected) }, () => [
902
+ vue.createTextVNode(vue.toDisplayString(vue.unref(selected).flag), 1)
903
+ ], true)
904
+ ], 8, _hoisted_3),
905
+ vue.createElementVNode("span", _hoisted_4, vue.toDisplayString(vue.unref(selected).code), 1),
906
+ !inactive.value && vue.unref(hasDropdown) ? (vue.openBlock(), vue.createElementBlock("svg", {
907
+ key: 0,
908
+ class: vue.normalizeClass(["pi-chevron", { "is-open": vue.unref(dropdownOpened) }]),
909
+ width: "12",
910
+ height: "12",
911
+ viewBox: "0 0 12 12",
912
+ fill: "none",
913
+ "aria-hidden": "true"
914
+ }, [..._cache[6] || (_cache[6] = [
915
+ vue.createElementVNode("path", {
916
+ d: "M2.5 4.5L6 8L9.5 4.5",
917
+ stroke: "currentColor",
918
+ "stroke-width": "1.5",
919
+ "stroke-linecap": "round",
920
+ "stroke-linejoin": "round"
921
+ }, null, -1)
922
+ ])], 2)) : vue.createCommentVNode("", true)
923
+ ], 10, _hoisted_2)
924
+ ]),
925
+ vue.createElementVNode("div", _hoisted_5, [
926
+ vue.createElementVNode("input", {
927
+ ref_key: "telRef",
928
+ ref: telRef,
929
+ type: "tel",
930
+ inputmode: "tel",
931
+ autocomplete: "tel-national",
932
+ autocorrect: "off",
933
+ autocapitalize: "off",
934
+ spellcheck: "false",
935
+ class: "pi-input",
936
+ placeholder: vue.unref(displayPlaceholder),
937
+ value: vue.unref(displayValue),
938
+ disabled: __props.disabled,
939
+ readonly: __props.readonly,
940
+ "aria-invalid": vue.unref(shouldShowWarn),
941
+ onBeforeinput: _cache[0] || (_cache[0] = //@ts-ignore
942
+ (...args) => vue.unref(mask).handleBeforeInput && vue.unref(mask).handleBeforeInput(...args)),
943
+ onInput,
944
+ onKeydown,
945
+ onPaste,
946
+ onFocus,
947
+ onBlur
948
+ }, null, 40, _hoisted_6),
949
+ vue.createElementVNode("div", _hoisted_7, [
950
+ vue.createVNode(vue.Transition, { name: "fade-scale" }, {
951
+ default: vue.withCtx(() => [
952
+ vue.renderSlot(_ctx.$slots, "actions-before", {}, void 0, true)
953
+ ]),
954
+ _: 3
955
+ }),
956
+ vue.createVNode(vue.Transition, { name: "fade-scale" }, {
957
+ default: vue.withCtx(() => [
958
+ showCopyButton.value ? (vue.openBlock(), vue.createElementBlock("button", {
959
+ key: 0,
960
+ type: "button",
961
+ class: vue.normalizeClass(["pi-btn", { "is-copied": vue.unref(copied) }]),
962
+ "aria-label": copyAriaLabel.value,
963
+ title: copyButtonTitle.value,
964
+ onClick: onCopyClick
965
+ }, [
966
+ slots["copy-svg"] ? vue.renderSlot(_ctx.$slots, "copy-svg", {
967
+ key: 0,
968
+ copied: vue.unref(copied)
969
+ }, void 0, true) : !vue.unref(copied) ? (vue.openBlock(), vue.createElementBlock("svg", _hoisted_9, [..._cache[7] || (_cache[7] = [
970
+ vue.createElementVNode("path", {
971
+ d: "M13.5 5.5V13.5H5.5V5.5H13.5ZM13.5 4H5.5C4.67 4 4 4.67 4 5.5V13.5C4 14.33 4.67 15 5.5 15H13.5C14.33 15 15 14.33 15 13.5V5.5C15 4.67 14.33 4 13.5 4ZM10.5 1H2.5V11H4V2.5H10.5V1Z",
972
+ fill: "currentColor"
973
+ }, null, -1)
974
+ ])])) : (vue.openBlock(), vue.createElementBlock("svg", _hoisted_10, [..._cache[8] || (_cache[8] = [
975
+ vue.createElementVNode("path", {
976
+ d: "M6.5 11.5L3 8L4.06 6.94L6.5 9.38L11.94 3.94L13 5L6.5 11.5Z",
977
+ fill: "currentColor"
978
+ }, null, -1)
979
+ ])]))
980
+ ], 10, _hoisted_8)) : vue.createCommentVNode("", true)
981
+ ]),
982
+ _: 3
983
+ }),
984
+ vue.createVNode(vue.Transition, { name: "fade-scale" }, {
985
+ default: vue.withCtx(() => [
986
+ showClearButton.value ? (vue.openBlock(), vue.createElementBlock("button", {
987
+ key: 0,
988
+ type: "button",
989
+ class: "pi-btn",
990
+ "aria-label": __props.clearButtonLabel,
991
+ title: __props.clearButtonLabel,
992
+ onClick: onClearClick
993
+ }, [
994
+ !slots["clear-svg"] ? (vue.openBlock(), vue.createElementBlock("svg", _hoisted_12, [..._cache[9] || (_cache[9] = [
995
+ vue.createElementVNode("path", {
996
+ d: "M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z",
997
+ fill: "currentColor"
998
+ }, null, -1)
999
+ ])])) : vue.renderSlot(_ctx.$slots, "clear-svg", { key: 1 }, void 0, true)
1000
+ ], 8, _hoisted_11)) : vue.createCommentVNode("", true)
1001
+ ]),
1002
+ _: 3
1003
+ })
1004
+ ])
1005
+ ]),
1006
+ (vue.openBlock(), vue.createBlock(vue.Teleport, { to: "body" }, [
1007
+ vue.createVNode(vue.Transition, { name: "dropdown" }, {
1008
+ default: vue.withCtx(() => [
1009
+ vue.unref(dropdownOpened) ? (vue.openBlock(), vue.createElementBlock("div", {
1010
+ key: 0,
1011
+ ref_key: "dropdownRef",
1012
+ ref: dropdownRef,
1013
+ class: vue.normalizeClass(["phone-dropdown", [__props.dropdownClass, themeClass.value]]),
1014
+ role: "dialog",
1015
+ "aria-modal": "false",
1016
+ "aria-label": "Select country",
1017
+ style: vue.normalizeStyle(dropdownStyle.value)
1018
+ }, [
1019
+ vue.createElementVNode("div", _hoisted_13, [
1020
+ vue.withDirectives(vue.createElementVNode("input", {
1021
+ ref_key: "searchRef",
1022
+ ref: searchRef,
1023
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => vue.isRef(search) ? search.value = $event : null),
1024
+ type: "search",
1025
+ class: "pi-search",
1026
+ "aria-label": "Search countries",
1027
+ placeholder: __props.searchPlaceholder,
1028
+ onKeydown: [
1029
+ _cache[2] || (_cache[2] = vue.withKeys(vue.withModifiers(($event) => vue.unref(focusNextOption)(scrollFocusedIntoView), ["prevent"]), ["down"])),
1030
+ _cache[3] || (_cache[3] = vue.withKeys(vue.withModifiers(($event) => vue.unref(focusPrevOption)(scrollFocusedIntoView), ["prevent"]), ["up"])),
1031
+ _cache[4] || (_cache[4] = vue.withKeys(vue.withModifiers(
1032
+ //@ts-ignore
1033
+ (...args) => vue.unref(chooseFocusedOption) && vue.unref(chooseFocusedOption)(...args),
1034
+ ["prevent"]
1035
+ ), ["enter"])),
1036
+ _cache[5] || (_cache[5] = vue.withKeys(
1037
+ //@ts-ignore
1038
+ (...args) => vue.unref(closeDropdown) && vue.unref(closeDropdown)(...args),
1039
+ ["escape"]
1040
+ ))
1041
+ ]
1042
+ }, null, 40, _hoisted_14), [
1043
+ [vue.vModelText, vue.unref(search)]
1044
+ ])
1045
+ ]),
1046
+ vue.createElementVNode("ul", {
1047
+ class: "pi-options",
1048
+ role: "listbox",
1049
+ "aria-activedescendant": `option-${vue.unref(focusedIndex)}`,
1050
+ tabindex: "-1"
1051
+ }, [
1052
+ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(filteredCountries), (c, idx) => {
1053
+ return vue.openBlock(), vue.createElementBlock("li", {
1054
+ id: `option-${idx}`,
1055
+ key: c.id,
1056
+ role: "option",
1057
+ class: vue.normalizeClass([
1058
+ "pi-option",
1059
+ {
1060
+ "is-focused": idx === vue.unref(focusedIndex),
1061
+ "is-selected": c.id === vue.unref(selected).id
1062
+ }
1063
+ ]),
1064
+ "aria-selected": c.id === vue.unref(selected).id,
1065
+ title: c.name,
1066
+ onClick: ($event) => onSelectCountry(c.id),
1067
+ onMouseenter: ($event) => focusedIndex.value = idx
1068
+ }, [
1069
+ vue.createElementVNode("span", {
1070
+ class: "pi-flag",
1071
+ role: "img",
1072
+ "aria-label": `${c.name} flag`
1073
+ }, [
1074
+ vue.renderSlot(_ctx.$slots, "flag", { country: c }, () => [
1075
+ vue.createTextVNode(vue.toDisplayString(c.flag), 1)
1076
+ ], true)
1077
+ ], 8, _hoisted_17),
1078
+ vue.createElementVNode("span", _hoisted_18, vue.toDisplayString(c.name), 1),
1079
+ vue.createElementVNode("span", _hoisted_19, vue.toDisplayString(c.code), 1)
1080
+ ], 42, _hoisted_16);
1081
+ }), 128)),
1082
+ vue.unref(filteredCountries).length === 0 ? (vue.openBlock(), vue.createElementBlock("li", _hoisted_20, vue.toDisplayString(__props.noResultsText), 1)) : vue.createCommentVNode("", true)
1083
+ ], 8, _hoisted_15)
1084
+ ], 6)) : vue.createCommentVNode("", true)
1085
+ ]),
1086
+ _: 3
1087
+ })
1088
+ ])),
1089
+ vue.createElementVNode("div", {
1090
+ ref_key: "liveRef",
1091
+ ref: liveRef,
1092
+ class: "sr-only",
1093
+ role: "status",
1094
+ "aria-live": "polite",
1095
+ "aria-atomic": "true"
1096
+ }, null, 512)
1097
+ ], 6);
1098
+ };
1099
+ }
1100
+ });
1101
+ const _export_sfc = (sfc, props) => {
1102
+ const target = sfc.__vccOpts || sfc;
1103
+ for (const [key, val] of props) {
1104
+ target[key] = val;
1105
+ }
1106
+ return target;
1107
+ };
1108
+ const PhoneInput = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-cd4ff552"]]);
1109
+ function getNavigatorLang() {
1110
+ if (typeof navigator !== "undefined") {
1111
+ return navigator.language || "";
1112
+ }
1113
+ return "";
1114
+ }
1115
+ function getCountry(countryCode, locale) {
1116
+ const isEn = locale.toLowerCase().startsWith("en");
1117
+ const countriesMap = isEn ? m : MasksFullMap(locale);
1118
+ const id = countryCode.toUpperCase();
1119
+ const found = countriesMap[id];
1120
+ return found ? { id, ...found } : null;
1121
+ }
1122
+ function getDefaultCountry(locale) {
1123
+ const isEn = locale.toLowerCase().startsWith("en");
1124
+ const countries = isEn ? m : MasksFullMap(locale);
1125
+ return { id: "US", ...countries.US };
1126
+ }
1127
+ async function detectCountryFromGeoIP() {
1128
+ try {
1129
+ const controller = new AbortController();
1130
+ const timeoutId = setTimeout(() => controller.abort(), GEO_IP_TIMEOUT);
1131
+ const res = await fetch(GEO_IP_URL, {
1132
+ signal: controller.signal,
1133
+ headers: { Accept: "application/json" }
1134
+ });
1135
+ clearTimeout(timeoutId);
1136
+ if (!res.ok) return null;
1137
+ const json = await res.json();
1138
+ const code = (json.country || json.country_code || json.countryCode || json.country_code2 || "").toString().toUpperCase();
1139
+ return code || null;
1140
+ } catch {
1141
+ return null;
1142
+ }
1143
+ }
1144
+ function detectCountryFromLocale() {
1145
+ try {
1146
+ const lang = getNavigatorLang();
1147
+ try {
1148
+ const loc = new Intl.Locale(lang);
1149
+ if (loc.region) return loc.region.toUpperCase();
1150
+ } catch {
1151
+ }
1152
+ const parts = lang.split(/[-_]/);
1153
+ if (parts.length > 1) return parts[1]?.toUpperCase() || null;
1154
+ } catch {
1155
+ }
1156
+ return null;
1157
+ }
1158
+ async function initState(binding) {
1159
+ const value = binding.value;
1160
+ let options = {};
1161
+ if (typeof value === "string") {
1162
+ options = { country: value };
1163
+ } else if (typeof value === "object" && value !== null) {
1164
+ options = value;
1165
+ }
1166
+ const locale = options.locale || getNavigatorLang() || "en";
1167
+ let country = null;
1168
+ if (options.country) {
1169
+ country = getCountry(options.country, locale);
1170
+ } else if (options.detect) {
1171
+ const geoCountry = await detectCountryFromGeoIP();
1172
+ if (geoCountry) {
1173
+ country = getCountry(geoCountry, locale);
1174
+ }
1175
+ if (!country) {
1176
+ const localeCountry = detectCountryFromLocale();
1177
+ if (localeCountry) {
1178
+ country = getCountry(localeCountry, locale);
1179
+ }
1180
+ }
1181
+ }
1182
+ if (!country) {
1183
+ country = getDefaultCountry(locale);
1184
+ }
1185
+ return {
1186
+ country,
1187
+ formatter: createPhoneFormatter(country),
1188
+ digits: "",
1189
+ locale,
1190
+ options
1191
+ };
1192
+ }
1193
+ function updateDisplay(el, state) {
1194
+ el.value = state.formatter.formatDisplay(state.digits);
1195
+ if (state.options.onChange) {
1196
+ const fullNumberFormatted = `${state.country.code} ${el.value}`;
1197
+ const fullNumber = `${state.country.code}${state.digits}`;
1198
+ state.options.onChange({
1199
+ full: fullNumber,
1200
+ fullFormatted: fullNumberFormatted,
1201
+ digits: state.digits
1202
+ });
1203
+ }
1204
+ }
1205
+ function createBeforeInputHandler(el) {
1206
+ return (e) => {
1207
+ const data = e.data;
1208
+ if (e.inputType !== "insertText" || !data) return;
1209
+ if (InvalidPattern.test(data) || data === " " && el.value.endsWith(" ")) {
1210
+ e.preventDefault();
1211
+ }
1212
+ };
1213
+ }
1214
+ function createInputHandler(el, state) {
1215
+ return (e) => {
1216
+ const target = e.target;
1217
+ if (!target) return;
1218
+ const raw = target.value || "";
1219
+ const maxDigits = state.formatter.getMaxDigits();
1220
+ state.digits = extractDigits(raw, maxDigits);
1221
+ updateDisplay(el, state);
1222
+ vue.nextTick(() => {
1223
+ const pos = state.formatter.getCaretPosition(state.digits.length);
1224
+ setCaret(el, pos);
1225
+ });
1226
+ };
1227
+ }
1228
+ function createKeydownHandler(el, state) {
1229
+ return (e) => {
1230
+ if (e.ctrlKey || e.metaKey || e.altKey || NavigationKeys.includes(e.key)) return;
1231
+ const [selStart, selEnd] = getSelection(el);
1232
+ if (e.key === "Backspace") {
1233
+ e.preventDefault();
1234
+ if (selStart !== selEnd) {
1235
+ const range = state.formatter.getDigitRange(state.digits, selStart, selEnd);
1236
+ if (range) {
1237
+ const [start, end] = range;
1238
+ state.digits = state.digits.slice(0, start) + state.digits.slice(end);
1239
+ updateDisplay(el, state);
1240
+ vue.nextTick(() => {
1241
+ const pos = state.formatter.getCaretPosition(start);
1242
+ setCaret(el, pos);
1243
+ });
1244
+ }
1245
+ return;
1246
+ }
1247
+ if (selStart > 0) {
1248
+ const displayStr = el.value;
1249
+ let prevPos = selStart - 1;
1250
+ while (prevPos >= 0 && Delimiters.includes(displayStr[prevPos])) {
1251
+ prevPos--;
1252
+ }
1253
+ if (prevPos >= 0) {
1254
+ const range = state.formatter.getDigitRange(state.digits, prevPos, prevPos + 1);
1255
+ if (range) {
1256
+ const [start] = range;
1257
+ state.digits = state.digits.slice(0, start) + state.digits.slice(start + 1);
1258
+ updateDisplay(el, state);
1259
+ vue.nextTick(() => {
1260
+ const pos = state.formatter.getCaretPosition(start);
1261
+ setCaret(el, pos);
1262
+ });
1263
+ }
1264
+ }
1265
+ }
1266
+ return;
1267
+ }
1268
+ if (e.key === "Delete") {
1269
+ e.preventDefault();
1270
+ if (selStart !== selEnd) {
1271
+ const range = state.formatter.getDigitRange(state.digits, selStart, selEnd);
1272
+ if (range) {
1273
+ const [start, end] = range;
1274
+ state.digits = state.digits.slice(0, start) + state.digits.slice(end);
1275
+ updateDisplay(el, state);
1276
+ vue.nextTick(() => {
1277
+ const pos = state.formatter.getCaretPosition(start);
1278
+ setCaret(el, pos);
1279
+ });
1280
+ }
1281
+ return;
1282
+ }
1283
+ if (selStart < el.value.length) {
1284
+ const range = state.formatter.getDigitRange(state.digits, selStart, selStart + 1);
1285
+ if (range) {
1286
+ const [start] = range;
1287
+ state.digits = state.digits.slice(0, start) + state.digits.slice(start + 1);
1288
+ updateDisplay(el, state);
1289
+ vue.nextTick(() => {
1290
+ const pos = state.formatter.getCaretPosition(start);
1291
+ setCaret(el, pos);
1292
+ });
1293
+ }
1294
+ }
1295
+ return;
1296
+ }
1297
+ if (/^[0-9]$/.test(e.key)) {
1298
+ if (state.digits.length >= state.formatter.getMaxDigits()) {
1299
+ e.preventDefault();
1300
+ }
1301
+ return;
1302
+ }
1303
+ if (e.key.length === 1) {
1304
+ e.preventDefault();
1305
+ }
1306
+ };
1307
+ }
1308
+ function createPasteHandler(el, state) {
1309
+ return (e) => {
1310
+ e.preventDefault();
1311
+ const text = e.clipboardData?.getData("text") || "";
1312
+ const maxDigits = state.formatter.getMaxDigits();
1313
+ const pastedDigits = extractDigits(text, maxDigits);
1314
+ if (pastedDigits.length === 0) return;
1315
+ const [selStart, selEnd] = getSelection(el);
1316
+ if (selStart !== selEnd) {
1317
+ const range2 = state.formatter.getDigitRange(state.digits, selStart, selEnd);
1318
+ if (range2) {
1319
+ const [start, end] = range2;
1320
+ const left2 = state.digits.slice(0, start);
1321
+ const right2 = state.digits.slice(end);
1322
+ state.digits = extractDigits(left2 + pastedDigits + right2, maxDigits);
1323
+ updateDisplay(el, state);
1324
+ vue.nextTick(() => {
1325
+ const pos = state.formatter.getCaretPosition(start + pastedDigits.length);
1326
+ setCaret(el, pos);
1327
+ });
1328
+ return;
1329
+ }
1330
+ }
1331
+ const range = state.formatter.getDigitRange(state.digits, selStart, selStart);
1332
+ const insertIndex = range ? range[0] : state.digits.length;
1333
+ const left = state.digits.slice(0, insertIndex);
1334
+ const right = state.digits.slice(insertIndex);
1335
+ state.digits = extractDigits(left + pastedDigits + right, maxDigits);
1336
+ updateDisplay(el, state);
1337
+ vue.nextTick(() => {
1338
+ const pos = state.formatter.getCaretPosition(insertIndex + pastedDigits.length);
1339
+ setCaret(el, pos);
1340
+ });
1341
+ };
1342
+ }
1343
+ async function updateCountry(el, state, newCountryCode) {
1344
+ const newCountry = getCountry(newCountryCode, state.locale);
1345
+ if (!newCountry) return;
1346
+ state.country = newCountry;
1347
+ state.formatter = createPhoneFormatter(newCountry);
1348
+ el.placeholder = state.formatter.getPlaceholder();
1349
+ const maxDigits = state.formatter.getMaxDigits();
1350
+ if (state.digits.length > maxDigits) {
1351
+ state.digits = state.digits.slice(0, maxDigits);
1352
+ }
1353
+ updateDisplay(el, state);
1354
+ if (state.options.onCountryChange) {
1355
+ state.options.onCountryChange(newCountry);
1356
+ }
1357
+ }
1358
+ const vPhoneMask = {
1359
+ async mounted(el, binding) {
1360
+ if (el.tagName !== "INPUT") {
1361
+ console.warn("[v-phone-mask] Directive can only be used on input elements");
1362
+ return;
1363
+ }
1364
+ el.setAttribute("type", "tel");
1365
+ el.setAttribute("inputmode", "tel");
1366
+ const state = await initState(binding);
1367
+ el.__phoneMaskState = state;
1368
+ state.inputHandler = createInputHandler(el, state);
1369
+ state.keydownHandler = createKeydownHandler(el, state);
1370
+ state.pasteHandler = createPasteHandler(el, state);
1371
+ state.beforeInputHandler = createBeforeInputHandler(el);
1372
+ el.addEventListener("beforeinput", state.beforeInputHandler);
1373
+ el.addEventListener("input", state.inputHandler);
1374
+ el.addEventListener("keydown", state.keydownHandler);
1375
+ el.addEventListener("paste", state.pasteHandler);
1376
+ el.setAttribute("placeholder", state.formatter.getPlaceholder());
1377
+ if (state.options.onCountryChange) {
1378
+ state.options.onCountryChange(state.country);
1379
+ }
1380
+ if (el.value) {
1381
+ const maxDigits = state.formatter.getMaxDigits();
1382
+ state.digits = extractDigits(el.value, maxDigits);
1383
+ updateDisplay(el, state);
1384
+ }
1385
+ },
1386
+ async updated(el, binding) {
1387
+ const state = el.__phoneMaskState;
1388
+ if (!state) return;
1389
+ const value = binding.value;
1390
+ let newOptions = {};
1391
+ if (typeof value === "string") {
1392
+ newOptions = { country: value };
1393
+ } else if (typeof value === "object" && value !== null) {
1394
+ newOptions = value;
1395
+ }
1396
+ const oldCountry = state.options.country;
1397
+ state.options = newOptions;
1398
+ const newCountry = newOptions.country;
1399
+ if (newCountry && newCountry !== oldCountry) {
1400
+ await updateCountry(el, state, newCountry);
1401
+ }
1402
+ const newDigits = extractDigits(el.value);
1403
+ if (newDigits !== state.digits) {
1404
+ state.digits = newDigits;
1405
+ updateDisplay(el, state);
1406
+ }
1407
+ },
1408
+ unmounted(el) {
1409
+ const state = el.__phoneMaskState;
1410
+ if (!state) return;
1411
+ if (state.beforeInputHandler) el.removeEventListener("beforeinput", state.beforeInputHandler);
1412
+ if (state.inputHandler) el.removeEventListener("input", state.inputHandler);
1413
+ if (state.keydownHandler) el.removeEventListener("keydown", state.keydownHandler);
1414
+ if (state.pasteHandler) el.removeEventListener("paste", state.pasteHandler);
1415
+ delete el.__phoneMaskState;
1416
+ }
1417
+ };
1418
+ function install(app) {
1419
+ app.component("PhoneInput", PhoneInput);
1420
+ app.directive("phone-mask", vPhoneMask);
1421
+ }
1422
+ const index = {
1423
+ install
1424
+ };
1425
+ const PMaskHelpers = {
1426
+ getFlagEmoji: g,
1427
+ countPlaceholders,
1428
+ formatDigitsWithMap,
1429
+ pickMaskVariant,
1430
+ removeCountryCodePrefix,
1431
+ toArray
1432
+ };
1433
+ exports.PMaskHelpers = PMaskHelpers;
1434
+ exports.PhoneInput = PhoneInput;
1435
+ exports.default = index;
1436
+ exports.install = install;
1437
+ exports.vPhoneMask = vPhoneMask;
1438
+ exports.vPhoneMaskSetCountry = updateCountry;
1439
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
1440
+ return exports;
1441
+ })({}, Vue);