@desource/phone-mask-vue 0.3.0 → 1.0.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/CHANGELOG.md +34 -0
- package/README.md +153 -10
- package/dist/index.cjs +824 -885
- package/dist/index.js +824 -885
- package/dist/index.mjs +825 -886
- package/dist/phone-mask-vue.css +64 -64
- package/dist/types/components/PhoneInput.vue.d.ts +7 -7
- package/dist/types/components/PhoneInput.vue.d.ts.map +1 -1
- package/dist/types/composables/internal/useCopyAction.d.ts +14 -0
- package/dist/types/composables/internal/useCopyAction.d.ts.map +1 -0
- package/dist/types/composables/internal/useCountry.d.ts +22 -0
- package/dist/types/composables/internal/useCountry.d.ts.map +1 -0
- package/dist/types/composables/internal/useCountrySelector.d.ts +30 -0
- package/dist/types/composables/internal/useCountrySelector.d.ts.map +1 -0
- package/dist/types/composables/internal/useFormatter.d.ts +46 -0
- package/dist/types/composables/internal/useFormatter.d.ts.map +1 -0
- package/dist/types/composables/internal/useInputHandlers.d.ts +20 -0
- package/dist/types/composables/internal/useInputHandlers.d.ts.map +1 -0
- package/dist/types/composables/internal/useTheme.d.ts +10 -0
- package/dist/types/composables/internal/useTheme.d.ts.map +1 -0
- package/dist/types/composables/internal/useValidationHint.d.ts +6 -0
- package/dist/types/composables/internal/useValidationHint.d.ts.map +1 -0
- package/dist/types/composables/usePhoneMask.d.ts +8 -0
- package/dist/types/composables/usePhoneMask.d.ts.map +1 -0
- package/dist/types/composables/{useClipboard.d.ts → utility/useClipboard.d.ts} +1 -2
- package/dist/types/composables/utility/useClipboard.d.ts.map +1 -0
- package/dist/types/composables/utility/useTimer.d.ts +9 -0
- package/dist/types/composables/utility/useTimer.d.ts.map +1 -0
- package/dist/types/directives/vPhoneMask.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types.d.ts +31 -9
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +15 -9
- package/dist/types/composables/useClipboard.d.ts.map +0 -1
- package/dist/types/composables/useCountrySelector.d.ts +0 -21
- package/dist/types/composables/useCountrySelector.d.ts.map +0 -1
- package/dist/types/composables/useMask.d.ts +0 -20
- package/dist/types/composables/useMask.d.ts.map +0 -1
- package/dist/types/composables/usePhoneFormatter.d.ts +0 -16
- package/dist/types/composables/usePhoneFormatter.d.ts.map +0 -1
- package/dist/types/consts.d.ts +0 -4
- package/dist/types/consts.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -9,67 +9,92 @@ var lib = (function(exports, vue) {
|
|
|
9
9
|
const e2 = [...o2.toUpperCase()].map((t2) => (t2.codePointAt(0) ?? 0) + 127397);
|
|
10
10
|
return String.fromCodePoint(...e2);
|
|
11
11
|
};
|
|
12
|
-
const
|
|
12
|
+
const o$1 = "en", n = /* @__PURE__ */ new Map(), getDisplayNames = (e2) => {
|
|
13
|
+
const t2 = e2.toLowerCase(), s2 = n.get(t2);
|
|
14
|
+
if (s2) return s2;
|
|
15
|
+
const r = new Intl.DisplayNames([e2], { type: "region" });
|
|
16
|
+
if (n.size >= 10) {
|
|
17
|
+
for (const e3 of n.keys()) if (e3 !== o$1) {
|
|
18
|
+
n.delete(e3);
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return n.set(t2, r), r;
|
|
23
|
+
}, s$1 = Object.entries(M), divideMask = (e2) => e2.split(/ (.*)/s);
|
|
13
24
|
function getCodeAndMask(e2) {
|
|
14
|
-
let
|
|
25
|
+
let t2 = "", o2 = "";
|
|
15
26
|
if (Array.isArray(e2)) {
|
|
16
|
-
const
|
|
17
|
-
for (const
|
|
18
|
-
const [e3, s2] = divideMask(
|
|
19
|
-
|
|
27
|
+
const n2 = [];
|
|
28
|
+
for (const o3 of e2) {
|
|
29
|
+
const [e3, s2] = divideMask(o3);
|
|
30
|
+
t2 || (t2 = e3), n2.push(s2);
|
|
20
31
|
}
|
|
21
|
-
|
|
32
|
+
o2 = n2;
|
|
22
33
|
} else {
|
|
23
|
-
const [
|
|
24
|
-
|
|
34
|
+
const [n2, s2] = divideMask(e2);
|
|
35
|
+
t2 = n2, o2 = s2;
|
|
25
36
|
}
|
|
26
|
-
return [
|
|
37
|
+
return [t2, o2];
|
|
27
38
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const [
|
|
31
|
-
return e2[
|
|
39
|
+
s$1.map(([e2, t2]) => ({ id: e2, mask: t2 }));
|
|
40
|
+
s$1.reduce((e2, [t2, o2]) => {
|
|
41
|
+
const [n2, s2] = getCodeAndMask(o2);
|
|
42
|
+
return e2[t2] = { code: n2, mask: s2 }, e2;
|
|
32
43
|
}, {});
|
|
33
|
-
|
|
34
|
-
const [
|
|
35
|
-
return { id: e2, code:
|
|
44
|
+
s$1.map(([e2, t2]) => {
|
|
45
|
+
const [o2, n2] = getCodeAndMask(t2);
|
|
46
|
+
return { id: e2, code: o2, mask: n2 };
|
|
36
47
|
});
|
|
37
|
-
|
|
38
|
-
const [s2,
|
|
39
|
-
return e2[
|
|
48
|
+
s$1.reduce((e2, [o2, n2]) => {
|
|
49
|
+
const [s2, r] = getCodeAndMask(n2);
|
|
50
|
+
return e2[o2] = { code: s2, mask: r, flag: countryCodeEmoji(o2) }, e2;
|
|
40
51
|
}, {});
|
|
41
|
-
|
|
42
|
-
const [
|
|
43
|
-
return { id: e2, code:
|
|
52
|
+
s$1.map(([e2, o2]) => {
|
|
53
|
+
const [n2, s2] = getCodeAndMask(o2);
|
|
54
|
+
return { id: e2, code: n2, mask: s2, flag: countryCodeEmoji(e2) };
|
|
44
55
|
});
|
|
45
56
|
const MasksFullMap = (e2) => {
|
|
46
|
-
const o2 =
|
|
47
|
-
return
|
|
48
|
-
const [
|
|
49
|
-
return e3[
|
|
57
|
+
const o2 = getDisplayNames(e2);
|
|
58
|
+
return s$1.reduce((e3, [n2, s2]) => {
|
|
59
|
+
const [r, a] = getCodeAndMask(s2), d = o2.of(n2) ?? "";
|
|
60
|
+
return e3[n2] = { code: r, mask: a, name: d, flag: countryCodeEmoji(n2) }, e3;
|
|
50
61
|
}, {});
|
|
51
62
|
}, MasksFull = (e2) => {
|
|
52
|
-
const o2 =
|
|
53
|
-
return
|
|
54
|
-
const [s2,
|
|
55
|
-
return { id: e3, code: s2, mask:
|
|
63
|
+
const o2 = getDisplayNames(e2);
|
|
64
|
+
return s$1.map(([e3, n2]) => {
|
|
65
|
+
const [s2, r] = getCodeAndMask(n2);
|
|
66
|
+
return { id: e3, code: s2, mask: r, name: o2.of(e3) ?? "", flag: countryCodeEmoji(e3) };
|
|
56
67
|
});
|
|
57
|
-
},
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}, {}), i = t.map(([e2, t2]) => {
|
|
61
|
-
const [o2, s2] = getCodeAndMask(t2);
|
|
62
|
-
return { id: e2, code: o2, mask: s2, name: new Intl.DisplayNames(["en"], { type: "region" }).of(e2) ?? "", flag: countryCodeEmoji(e2) };
|
|
63
|
-
}), g = countryCodeEmoji;
|
|
68
|
+
}, f = MasksFullMap(o$1);
|
|
69
|
+
MasksFull(o$1);
|
|
70
|
+
const k = countryCodeEmoji;
|
|
64
71
|
function getNavigatorLang() {
|
|
65
72
|
return "undefined" != typeof navigator && navigator.language || "en";
|
|
66
73
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
74
|
+
function detectCountryFromLocale() {
|
|
75
|
+
try {
|
|
76
|
+
const t2 = getNavigatorLang();
|
|
77
|
+
try {
|
|
78
|
+
const e3 = new Intl.Locale(t2);
|
|
79
|
+
if (e3.region) return e3.region.toUpperCase();
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
const e2 = t2.split(/[-_]/);
|
|
83
|
+
if (e2.length > 1) return e2[1]?.toUpperCase() || null;
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
69
87
|
}
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
return
|
|
88
|
+
function hasCountry(t2) {
|
|
89
|
+
const r = f;
|
|
90
|
+
return t2.toUpperCase() in r;
|
|
91
|
+
}
|
|
92
|
+
function getCountry(e2, r) {
|
|
93
|
+
const n2 = MasksFullMap(r), o2 = e2.toUpperCase();
|
|
94
|
+
return o2 in n2 ? { id: o2, ...n2[o2] } : { id: "US", ...n2.US };
|
|
95
|
+
}
|
|
96
|
+
function parseCountryCode(t2, e2) {
|
|
97
|
+
return t2 && hasCountry(t2) ? t2.toUpperCase() : e2 || "";
|
|
73
98
|
}
|
|
74
99
|
function toArray(t2) {
|
|
75
100
|
return Array.isArray(t2) ? t2 : [t2];
|
|
@@ -80,480 +105,488 @@ var lib = (function(exports, vue) {
|
|
|
80
105
|
function removeCountryCodePrefix(t2) {
|
|
81
106
|
return t2.replace(/^\+\d+\s?/, "");
|
|
82
107
|
}
|
|
83
|
-
function pickMaskVariant(t2,
|
|
108
|
+
function pickMaskVariant(t2, e2) {
|
|
84
109
|
if (1 === t2.length) return t2[0];
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
const o2 =
|
|
110
|
+
const r = t2.map((t3) => ({ mask: t3, count: countPlaceholders(t3) })), n2 = r.filter((t3) => t3.count >= e2).sort((t3, e3) => t3.count - e3.count);
|
|
111
|
+
if (n2.length > 0) return n2[0].mask;
|
|
112
|
+
const o2 = r.sort((t3, e3) => e3.count - t3.count)[0];
|
|
88
113
|
return o2 ? o2.mask : t2[0];
|
|
89
114
|
}
|
|
90
|
-
function formatDigitsWithMap(t2,
|
|
91
|
-
let
|
|
92
|
-
const
|
|
115
|
+
function formatDigitsWithMap(t2, e2) {
|
|
116
|
+
let r = "";
|
|
117
|
+
const n2 = [];
|
|
93
118
|
let o2 = 0;
|
|
94
|
-
const a =
|
|
95
|
-
for (let
|
|
96
|
-
const
|
|
97
|
-
if ("#" ===
|
|
119
|
+
const a = e2.length, s2 = t2.length;
|
|
120
|
+
for (let c = 0; c < s2; c++) {
|
|
121
|
+
const s3 = t2[c];
|
|
122
|
+
if ("#" === s3) {
|
|
98
123
|
if (!(o2 < a)) break;
|
|
99
|
-
|
|
124
|
+
r += e2[o2], n2.push(o2), o2++;
|
|
100
125
|
} else {
|
|
101
|
-
const
|
|
102
|
-
(
|
|
126
|
+
const e3 = -1 !== t2.indexOf("#", c + 1) && o2 < a;
|
|
127
|
+
(r.length > 0 || e3) && (r += s3, n2.push(-1));
|
|
103
128
|
}
|
|
104
129
|
}
|
|
105
|
-
return { display:
|
|
130
|
+
return { display: r, map: n2 };
|
|
131
|
+
}
|
|
132
|
+
function filterCountries(t2, e2) {
|
|
133
|
+
const r = e2.trim().toUpperCase();
|
|
134
|
+
if (!r) return t2;
|
|
135
|
+
const n2 = r.replace(/\D/g, ""), o2 = n2.length > 0;
|
|
136
|
+
return t2.map((t3) => {
|
|
137
|
+
const e3 = t3.name.toUpperCase(), a = t3.id.toUpperCase(), s2 = t3.code.toUpperCase(), c = t3.code.replace(/\D/g, "");
|
|
138
|
+
let i2 = 0;
|
|
139
|
+
return e3.startsWith(r) ? i2 = 1e3 : e3.includes(r) && (i2 = 500), s2.startsWith(r) ? i2 += 100 : s2.includes(r) && (i2 += 50), a === r ? i2 += 200 : a.startsWith(r) && (i2 += 150), o2 && c.startsWith(n2) ? i2 += 80 : o2 && c.includes(n2) && (i2 += 40), { country: t3, score: i2 };
|
|
140
|
+
}).filter((t3) => t3.score > 0).sort((t3, e3) => e3.score !== t3.score ? e3.score - t3.score : t3.country.name.localeCompare(e3.country.name)).map((t3) => t3.country);
|
|
141
|
+
}
|
|
142
|
+
const t = [" ", "-", "(", ")"], e$1 = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "Tab"], i = /[^\d\s\-()]/;
|
|
143
|
+
function extractDigits(t2, e2) {
|
|
144
|
+
const i2 = t2.replace(/\D/g, "");
|
|
145
|
+
return e2 ? i2.slice(0, e2) : i2;
|
|
146
|
+
}
|
|
147
|
+
function getSelection(t2) {
|
|
148
|
+
return t2 ? [t2.selectionStart ?? 0, t2.selectionEnd ?? 0] : [0, 0];
|
|
149
|
+
}
|
|
150
|
+
function setCaret(t2, e2) {
|
|
151
|
+
if (t2) try {
|
|
152
|
+
t2.setSelectionRange(e2, e2);
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function processBeforeInput(t2) {
|
|
157
|
+
if (!t2.target) return;
|
|
158
|
+
const e2 = t2.target, n2 = t2.data;
|
|
159
|
+
"insertText" === t2.inputType && n2 && (i.test(n2) || " " === n2 && e2.value.endsWith(" ")) && t2.preventDefault();
|
|
160
|
+
}
|
|
161
|
+
function processInput(t2, e2) {
|
|
162
|
+
if (!t2.target) return;
|
|
163
|
+
const i2 = t2.target, { formatter: n2 } = e2, r = n2.getMaxDigits(), s2 = extractDigits(i2.value, r);
|
|
164
|
+
return { newDigits: s2, caretDigitIndex: s2.length };
|
|
165
|
+
}
|
|
166
|
+
function processKeydown(i2, n2) {
|
|
167
|
+
if (!i2.target) return;
|
|
168
|
+
const r = i2.target, { digits: s2, formatter: c } = n2;
|
|
169
|
+
if (i2.ctrlKey || i2.metaKey || i2.altKey || e$1.includes(i2.key)) return;
|
|
170
|
+
const [g, a] = getSelection(r);
|
|
171
|
+
if ("Backspace" !== i2.key) if ("Delete" !== i2.key) /^[0-9]$/.test(i2.key) ? s2.length >= c.getMaxDigits() && i2.preventDefault() : 1 === i2.key.length && i2.preventDefault();
|
|
172
|
+
else {
|
|
173
|
+
if (i2.preventDefault(), g !== a) {
|
|
174
|
+
const t2 = c.getDigitRange(s2, g, a);
|
|
175
|
+
if (t2) {
|
|
176
|
+
const [e2, i3] = t2;
|
|
177
|
+
return { newDigits: s2.slice(0, e2) + s2.slice(i3), caretDigitIndex: e2 };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (g < r.value.length) {
|
|
181
|
+
const t2 = c.getDigitRange(s2, g, r.value.length);
|
|
182
|
+
if (t2) {
|
|
183
|
+
const [e2] = t2;
|
|
184
|
+
return { newDigits: s2.slice(0, e2) + s2.slice(e2 + 1), caretDigitIndex: e2 };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
if (i2.preventDefault(), g !== a) {
|
|
190
|
+
const t2 = c.getDigitRange(s2, g, a);
|
|
191
|
+
if (t2) {
|
|
192
|
+
const [e2, i3] = t2;
|
|
193
|
+
return { newDigits: s2.slice(0, e2) + s2.slice(i3), caretDigitIndex: e2 };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (g > 0) {
|
|
197
|
+
const e2 = r.value;
|
|
198
|
+
let i3 = g - 1;
|
|
199
|
+
for (; i3 >= 0 && t.includes(e2[i3]); ) i3--;
|
|
200
|
+
if (i3 >= 0) {
|
|
201
|
+
const t2 = c.getDigitRange(s2, i3, i3 + 1);
|
|
202
|
+
if (t2) {
|
|
203
|
+
const [e3] = t2;
|
|
204
|
+
return { newDigits: s2.slice(0, e3) + s2.slice(e3 + 1), caretDigitIndex: e3 };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function processPaste(t2, e2) {
|
|
211
|
+
if (!t2.target) return;
|
|
212
|
+
t2.preventDefault();
|
|
213
|
+
const i2 = t2.target, { digits: n2, formatter: r } = e2, s2 = t2.clipboardData?.getData("text") || "", c = r.getMaxDigits(), g = extractDigits(s2, c);
|
|
214
|
+
if (0 === g.length) return;
|
|
215
|
+
const [a, o2] = getSelection(i2);
|
|
216
|
+
if (a !== o2) {
|
|
217
|
+
const t3 = r.getDigitRange(n2, a, o2);
|
|
218
|
+
if (t3) {
|
|
219
|
+
const [e3, i3] = t3;
|
|
220
|
+
return { newDigits: extractDigits(n2.slice(0, e3) + g + n2.slice(i3), c), caretDigitIndex: e3 + g.length };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const l = r.getDigitRange(n2, 0, a), f2 = l ? l[1] : 0;
|
|
224
|
+
return { newDigits: extractDigits(n2.slice(0, f2) + g + n2.slice(f2), c), caretDigitIndex: f2 + g.length };
|
|
225
|
+
}
|
|
226
|
+
function createPhoneFormatter(o2) {
|
|
227
|
+
const a = toArray(o2.mask), i2 = a.map((n2) => countPlaceholders(removeCountryCodePrefix(n2))), g = Math.max(...i2), getMask = (t2) => {
|
|
228
|
+
const n2 = pickMaskVariant(a, t2);
|
|
229
|
+
return removeCountryCodePrefix(n2);
|
|
230
|
+
};
|
|
231
|
+
return { formatDisplay: (t2) => {
|
|
232
|
+
const e2 = getMask(t2.length);
|
|
233
|
+
return formatDigitsWithMap(e2, t2).display;
|
|
234
|
+
}, getMaxDigits: () => g, getPlaceholder: () => getMask(0), getCaretPosition: (t2) => {
|
|
235
|
+
const e2 = getMask(t2), { display: r, map: l } = formatDigitsWithMap(e2, "0".repeat(t2));
|
|
236
|
+
for (let e3 = 0; e3 < l.length; e3++) if (l[e3] === t2) return e3;
|
|
237
|
+
if (t2 >= l.length) return r.length;
|
|
238
|
+
for (let e3 = 0; e3 < l.length; e3++) if (l[e3] > t2) return e3;
|
|
239
|
+
return r.length;
|
|
240
|
+
}, getDigitRange: (t2, e2, r) => {
|
|
241
|
+
const l = getMask(t2.length), { map: o3 } = formatDigitsWithMap(l, t2);
|
|
242
|
+
let a2 = 1 / 0, i3 = -1 / 0;
|
|
243
|
+
for (let t3 = e2; t3 < r && t3 < o3.length; t3++) {
|
|
244
|
+
const e3 = o3[t3];
|
|
245
|
+
void 0 !== e3 && e3 >= 0 && (a2 = Math.min(a2, e3), i3 = Math.max(i3, e3));
|
|
246
|
+
}
|
|
247
|
+
return a2 === 1 / 0 ? null : [a2, i3 + 1];
|
|
248
|
+
}, isComplete: (t2) => i2.includes(t2.length) };
|
|
106
249
|
}
|
|
107
250
|
const o = "https://ipapi.co/json/", e = 1500, p = "@desource/phone-mask:geo", s = 864e5;
|
|
108
|
-
async function detectCountryFromGeoIP(
|
|
109
|
-
const
|
|
251
|
+
async function detectCountryFromGeoIP(t2 = o, r = e) {
|
|
252
|
+
const n2 = new AbortController(), c = setTimeout(() => n2.abort(), r);
|
|
110
253
|
try {
|
|
111
|
-
const
|
|
112
|
-
if (clearTimeout(
|
|
113
|
-
const
|
|
114
|
-
return (
|
|
254
|
+
const o2 = await fetch(t2, { signal: n2.signal, headers: { Accept: "application/json" } });
|
|
255
|
+
if (clearTimeout(c), !o2.ok) return null;
|
|
256
|
+
const e2 = await o2.json();
|
|
257
|
+
return (e2.country || e2.country_code || e2.countryCode || e2.country_code2 || "").toString().toUpperCase() || null;
|
|
115
258
|
} catch {
|
|
116
|
-
return clearTimeout(
|
|
259
|
+
return clearTimeout(c), null;
|
|
117
260
|
}
|
|
118
261
|
}
|
|
119
|
-
async function detectByGeoIp(
|
|
262
|
+
async function detectByGeoIp() {
|
|
120
263
|
try {
|
|
121
264
|
const o3 = localStorage.getItem(p);
|
|
122
265
|
if (o3) {
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
|
|
266
|
+
const e3 = JSON.parse(o3), c = Date.now() - e3.ts > s, a = parseCountryCode(e3.country_code);
|
|
267
|
+
if (a && !c) return a;
|
|
268
|
+
localStorage.removeItem(p);
|
|
126
269
|
}
|
|
127
270
|
} catch {
|
|
128
271
|
}
|
|
129
|
-
const o2 = await detectCountryFromGeoIP();
|
|
130
|
-
if (
|
|
272
|
+
const o2 = await detectCountryFromGeoIP(), e2 = parseCountryCode(o2);
|
|
273
|
+
if (e2) {
|
|
131
274
|
try {
|
|
132
|
-
|
|
275
|
+
const t2 = JSON.stringify({ country_code: e2, ts: Date.now() });
|
|
276
|
+
localStorage.setItem(p, t2);
|
|
133
277
|
} catch {
|
|
134
278
|
}
|
|
135
|
-
return
|
|
279
|
+
return e2;
|
|
136
280
|
}
|
|
137
281
|
return null;
|
|
138
282
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const _id = id.toUpperCase();
|
|
156
|
-
return !!countriesMap.value[_id];
|
|
283
|
+
function useCountry({
|
|
284
|
+
country: countryOption,
|
|
285
|
+
locale: localeOption,
|
|
286
|
+
detect,
|
|
287
|
+
onCountryChange
|
|
288
|
+
} = {}) {
|
|
289
|
+
const locale = vue.computed(() => vue.toValue(localeOption) || getNavigatorLang());
|
|
290
|
+
const countryCode = vue.ref(parseCountryCode(vue.toValue(countryOption), "US"));
|
|
291
|
+
const country = vue.computed(() => getCountry(countryCode.value, locale.value));
|
|
292
|
+
const setCountry = (code) => {
|
|
293
|
+
const parsed = parseCountryCode(code);
|
|
294
|
+
if (parsed) {
|
|
295
|
+
countryCode.value = parsed;
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
157
299
|
};
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
else if (nameUpper.includes(q)) score = 500;
|
|
170
|
-
if (c.code.startsWith(q)) score += 100;
|
|
171
|
-
else if (c.code.includes(q)) score += 50;
|
|
172
|
-
if (idUpper === q) score += 200;
|
|
173
|
-
else if (idUpper.startsWith(q)) score += 150;
|
|
174
|
-
if (isNumericSearch && codeDigits.startsWith(qCodeDigits)) score += 80;
|
|
175
|
-
else if (isNumericSearch && codeDigits.includes(qCodeDigits)) score += 40;
|
|
176
|
-
return { country: c, score };
|
|
177
|
-
}).filter(({ score }) => score > 0).sort((a, b) => {
|
|
178
|
-
if (b.score !== a.score) return b.score - a.score;
|
|
179
|
-
return a.country.name.localeCompare(b.country.name);
|
|
180
|
-
}).map(({ country }) => country);
|
|
300
|
+
const detectCountry = async () => {
|
|
301
|
+
const geoCountry = await detectByGeoIp();
|
|
302
|
+
if (setCountry(geoCountry)) return;
|
|
303
|
+
const localeCountry = detectCountryFromLocale();
|
|
304
|
+
setCountry(localeCountry);
|
|
305
|
+
};
|
|
306
|
+
vue.watchEffect(() => {
|
|
307
|
+
const newCountry = vue.toValue(countryOption);
|
|
308
|
+
if (newCountry && newCountry !== countryCode.value) {
|
|
309
|
+
setCountry(newCountry);
|
|
310
|
+
}
|
|
181
311
|
});
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
312
|
+
vue.watchEffect(() => {
|
|
313
|
+
if (vue.toValue(detect) && !vue.toValue(countryOption)) {
|
|
314
|
+
detectCountry();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
vue.watchEffect(() => {
|
|
318
|
+
onCountryChange?.(country.value);
|
|
319
|
+
});
|
|
320
|
+
return { country, setCountry, locale };
|
|
321
|
+
}
|
|
322
|
+
function useFormatter({
|
|
323
|
+
country,
|
|
324
|
+
value,
|
|
325
|
+
onChange,
|
|
326
|
+
onPhoneChange,
|
|
327
|
+
onValidationChange
|
|
328
|
+
}) {
|
|
329
|
+
const formatter = vue.computed(() => createPhoneFormatter(vue.toValue(country)));
|
|
330
|
+
const maxDigits = vue.computed(() => formatter.value.getMaxDigits());
|
|
331
|
+
const digits = vue.computed(() => extractDigits(vue.toValue(value), maxDigits.value));
|
|
332
|
+
const displayPlaceholder = vue.computed(() => formatter.value.getPlaceholder());
|
|
333
|
+
const displayValue = vue.computed(() => formatter.value.formatDisplay(digits.value));
|
|
334
|
+
const full = vue.computed(() => digits.value ? `${vue.toValue(country).code}${digits.value}` : "");
|
|
335
|
+
const fullFormatted = vue.computed(() => displayValue.value ? `${vue.toValue(country).code} ${displayValue.value}` : "");
|
|
336
|
+
const isComplete = vue.computed(() => formatter.value.isComplete(digits.value));
|
|
337
|
+
const isEmpty = vue.computed(() => digits.value.length === 0);
|
|
338
|
+
const shouldShowWarn = vue.computed(() => !isEmpty.value && !isComplete.value);
|
|
339
|
+
const phoneData = vue.computed(() => ({
|
|
340
|
+
full: full.value,
|
|
341
|
+
fullFormatted: fullFormatted.value,
|
|
342
|
+
digits: digits.value
|
|
343
|
+
}));
|
|
344
|
+
vue.watchEffect(() => {
|
|
345
|
+
if (vue.toValue(value) !== digits.value) {
|
|
346
|
+
onChange(digits.value);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
vue.watchEffect(() => {
|
|
350
|
+
onPhoneChange?.(phoneData.value);
|
|
351
|
+
});
|
|
352
|
+
vue.watchEffect(() => {
|
|
353
|
+
onValidationChange?.(isComplete.value);
|
|
354
|
+
});
|
|
355
|
+
return {
|
|
356
|
+
digits,
|
|
357
|
+
formatter,
|
|
358
|
+
displayPlaceholder,
|
|
359
|
+
displayValue,
|
|
360
|
+
full,
|
|
361
|
+
fullFormatted,
|
|
362
|
+
isComplete,
|
|
363
|
+
isEmpty,
|
|
364
|
+
shouldShowWarn
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
function useTimer() {
|
|
368
|
+
let timerRef = null;
|
|
369
|
+
const clear = () => {
|
|
370
|
+
if (timerRef) {
|
|
371
|
+
clearTimeout(timerRef);
|
|
372
|
+
timerRef = null;
|
|
373
|
+
}
|
|
185
374
|
};
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
await vue.nextTick();
|
|
190
|
-
searchRef.value?.focus({ preventScroll: true });
|
|
191
|
-
focusedIndex.value = 0;
|
|
375
|
+
const set = (callback, delay) => {
|
|
376
|
+
clear();
|
|
377
|
+
timerRef = setTimeout(callback, delay);
|
|
192
378
|
};
|
|
193
|
-
|
|
194
|
-
|
|
379
|
+
vue.onUnmounted(clear);
|
|
380
|
+
return { set, clear };
|
|
381
|
+
}
|
|
382
|
+
function useValidationHint() {
|
|
383
|
+
const showValidationHint = vue.ref(false);
|
|
384
|
+
const validationTimer = useTimer();
|
|
385
|
+
const clearValidationHint = (hideHint = true) => {
|
|
386
|
+
if (hideHint) showValidationHint.value = false;
|
|
387
|
+
validationTimer.clear();
|
|
195
388
|
};
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
389
|
+
const scheduleValidationHint = (delay) => {
|
|
390
|
+
showValidationHint.value = false;
|
|
391
|
+
validationTimer.set(() => {
|
|
392
|
+
showValidationHint.value = true;
|
|
393
|
+
}, delay);
|
|
200
394
|
};
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
395
|
+
return { showValidationHint, clearValidationHint, scheduleValidationHint };
|
|
396
|
+
}
|
|
397
|
+
const HINT_DELAY_INPUT = 500;
|
|
398
|
+
const HINT_DELAY_ACTION = 300;
|
|
399
|
+
function useInputHandlers(options) {
|
|
400
|
+
const { formatter, digits, inactive, onChange, scheduleValidationHint } = options;
|
|
401
|
+
const scheduleCaretUpdate = (el, digitIndex) => {
|
|
402
|
+
vue.nextTick(() => {
|
|
403
|
+
if (!el) return;
|
|
404
|
+
const pos = vue.toValue(formatter).getCaretPosition(digitIndex);
|
|
405
|
+
setCaret(el, pos);
|
|
406
|
+
});
|
|
205
407
|
};
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
if (item) selectCountry(item.id);
|
|
408
|
+
const handleBeforeInput = (e2) => {
|
|
409
|
+
processBeforeInput(e2);
|
|
209
410
|
};
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
const parts = lang.split(/[-_]/);
|
|
219
|
-
if (parts.length > 1 && hasCountry(parts[1])) return parts[1].toUpperCase();
|
|
220
|
-
} catch {
|
|
221
|
-
}
|
|
222
|
-
return null;
|
|
411
|
+
const handleInput = (e2) => {
|
|
412
|
+
if (vue.toValue(inactive)) return;
|
|
413
|
+
const result = processInput(e2, { formatter: vue.toValue(formatter) });
|
|
414
|
+
if (!result) return;
|
|
415
|
+
onChange?.(result.newDigits);
|
|
416
|
+
scheduleCaretUpdate(e2.target, result.caretDigitIndex);
|
|
417
|
+
scheduleValidationHint?.(HINT_DELAY_INPUT);
|
|
223
418
|
};
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (
|
|
419
|
+
const handleKeydown = (e2) => {
|
|
420
|
+
if (vue.toValue(inactive)) return;
|
|
421
|
+
const result = processKeydown(e2, { digits: vue.toValue(digits), formatter: vue.toValue(formatter) });
|
|
422
|
+
if (!result) return;
|
|
423
|
+
onChange?.(result.newDigits);
|
|
424
|
+
scheduleCaretUpdate(e2.target, result.caretDigitIndex);
|
|
425
|
+
scheduleValidationHint?.(HINT_DELAY_ACTION);
|
|
228
426
|
};
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const geo = await detectByGeoIp(hasCountry);
|
|
237
|
-
if (geo) {
|
|
238
|
-
selectInitialCountry(geo, emitFn);
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
const loc = detectFromLocale();
|
|
242
|
-
if (loc) {
|
|
243
|
-
selectInitialCountry(loc, emitFn);
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
427
|
+
const handlePaste = (e2) => {
|
|
428
|
+
if (vue.toValue(inactive)) return;
|
|
429
|
+
const result = processPaste(e2, { digits: vue.toValue(digits), formatter: vue.toValue(formatter) });
|
|
430
|
+
if (!result) return;
|
|
431
|
+
onChange?.(result.newDigits);
|
|
432
|
+
scheduleCaretUpdate(e2.target, result.caretDigitIndex);
|
|
433
|
+
scheduleValidationHint?.(HINT_DELAY_ACTION);
|
|
246
434
|
};
|
|
247
435
|
return {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// Dropdown
|
|
253
|
-
hasDropdown,
|
|
254
|
-
dropdownOpened,
|
|
255
|
-
search,
|
|
256
|
-
focusedIndex,
|
|
257
|
-
filteredCountries,
|
|
258
|
-
selectCountry,
|
|
259
|
-
toggleDropdown,
|
|
260
|
-
closeDropdown,
|
|
261
|
-
focusNextOption,
|
|
262
|
-
focusPrevOption,
|
|
263
|
-
chooseFocusedOption,
|
|
264
|
-
// Country Detection
|
|
265
|
-
initCountry
|
|
436
|
+
handleBeforeInput,
|
|
437
|
+
handleInput,
|
|
438
|
+
handleKeydown,
|
|
439
|
+
handlePaste
|
|
266
440
|
};
|
|
267
441
|
}
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
442
|
+
function useCountrySelector({
|
|
443
|
+
rootRef,
|
|
444
|
+
dropdownRef,
|
|
445
|
+
searchRef,
|
|
446
|
+
selectorRef,
|
|
447
|
+
locale,
|
|
448
|
+
countryOption,
|
|
449
|
+
inactive,
|
|
450
|
+
onSelectCountry,
|
|
451
|
+
onAfterSelect
|
|
452
|
+
}) {
|
|
453
|
+
const search = vue.ref("");
|
|
454
|
+
const dropdownOpen = vue.ref(false);
|
|
455
|
+
const dropdownStyle = vue.shallowRef({});
|
|
456
|
+
const focusedIndex = vue.ref(0);
|
|
457
|
+
const countries = vue.computed(() => MasksFull(vue.toValue(locale)));
|
|
458
|
+
const filteredCountries = vue.computed(() => filterCountries(countries.value, search.value));
|
|
459
|
+
const hasDropdown = vue.computed(() => !vue.toValue(countryOption) && countries.value.length > 1);
|
|
460
|
+
const setFocusedIndex = (index2) => {
|
|
461
|
+
focusedIndex.value = index2;
|
|
275
462
|
};
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
for (let i2 = 0; i2 < map.length; i2++) {
|
|
294
|
-
if (map[i2] > digitIndex) return i2;
|
|
295
|
-
}
|
|
296
|
-
return display.length;
|
|
297
|
-
},
|
|
298
|
-
getDigitRange: (digits, selStart, selEnd) => {
|
|
299
|
-
const template = getMask(digits.length);
|
|
300
|
-
const { map } = formatDigitsWithMap(template, digits);
|
|
301
|
-
let min = Infinity;
|
|
302
|
-
let max = -Infinity;
|
|
303
|
-
for (let i2 = selStart; i2 < selEnd && i2 < map.length; i2++) {
|
|
304
|
-
const digitIdx = map[i2];
|
|
305
|
-
if (digitIdx !== void 0 && digitIdx >= 0) {
|
|
306
|
-
min = Math.min(min, digitIdx);
|
|
307
|
-
max = Math.max(max, digitIdx);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return min === Infinity ? null : [min, max + 1];
|
|
311
|
-
},
|
|
312
|
-
isComplete: (digits) => {
|
|
313
|
-
return variantsDigits.includes(digits.length);
|
|
463
|
+
const focusSearch = () => {
|
|
464
|
+
vue.nextTick(() => searchRef.value?.focus({ preventScroll: true }));
|
|
465
|
+
};
|
|
466
|
+
const closeDropdown = () => {
|
|
467
|
+
dropdownOpen.value = false;
|
|
468
|
+
};
|
|
469
|
+
const openDropdown = () => {
|
|
470
|
+
dropdownOpen.value = true;
|
|
471
|
+
setFocusedIndex(0);
|
|
472
|
+
focusSearch();
|
|
473
|
+
};
|
|
474
|
+
const toggleDropdown = () => {
|
|
475
|
+
if (vue.toValue(inactive) || !hasDropdown.value) return;
|
|
476
|
+
if (dropdownOpen.value) {
|
|
477
|
+
closeDropdown();
|
|
478
|
+
} else {
|
|
479
|
+
openDropdown();
|
|
314
480
|
}
|
|
315
481
|
};
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function extractDigits(value, maxLength) {
|
|
325
|
-
const digits = value.replace(/\D/g, "");
|
|
326
|
-
return maxLength ? digits.slice(0, maxLength) : digits;
|
|
327
|
-
}
|
|
328
|
-
function getSelection(el) {
|
|
329
|
-
if (!el) return [0, 0];
|
|
330
|
-
return [el.selectionStart ?? 0, el.selectionEnd ?? 0];
|
|
331
|
-
}
|
|
332
|
-
const Delimiters = [" ", "-", "(", ")"];
|
|
333
|
-
const NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "Tab"];
|
|
334
|
-
const InvalidPattern = /[^\d\s\-()]/;
|
|
335
|
-
function useMask(selected, telRef) {
|
|
336
|
-
const digits = vue.ref("");
|
|
337
|
-
const displayValue = vue.ref("");
|
|
338
|
-
const validationTimer = vue.ref(null);
|
|
339
|
-
const showValidationHint = vue.ref(false);
|
|
340
|
-
const formatter = vue.computed(() => createPhoneFormatter(selected.value));
|
|
341
|
-
const displayPlaceholder = vue.computed(() => formatter.value.getPlaceholder());
|
|
342
|
-
const isComplete = vue.computed(() => formatter.value.isComplete(digits.value));
|
|
343
|
-
const isEmpty = vue.computed(() => digits.value.length === 0);
|
|
344
|
-
const maxDigits = vue.computed(() => formatter.value.getMaxDigits());
|
|
345
|
-
const shouldShowWarn = vue.computed(() => showValidationHint.value && !isEmpty.value && !isComplete.value);
|
|
346
|
-
const fullFormatted = vue.computed(() => {
|
|
347
|
-
if (!displayValue.value) return "";
|
|
348
|
-
return `${selected.value.code} ${displayValue.value}`;
|
|
349
|
-
});
|
|
350
|
-
const full = vue.computed(() => {
|
|
351
|
-
if (!digits.value) return "";
|
|
352
|
-
return `${selected.value.code}${digits.value}`;
|
|
353
|
-
});
|
|
354
|
-
const updateDisplay2 = () => {
|
|
355
|
-
displayValue.value = formatter.value.formatDisplay(digits.value);
|
|
482
|
+
const selectCountry = (code) => {
|
|
483
|
+
onSelectCountry(code);
|
|
484
|
+
closeDropdown();
|
|
485
|
+
search.value = "";
|
|
486
|
+
setFocusedIndex(0);
|
|
487
|
+
onAfterSelect?.();
|
|
356
488
|
};
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
489
|
+
const handleSearchChange = (e2) => {
|
|
490
|
+
search.value = e2.target.value;
|
|
491
|
+
setFocusedIndex(0);
|
|
360
492
|
};
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
493
|
+
const onDocClick = (ev) => {
|
|
494
|
+
const target = ev.target;
|
|
495
|
+
const dropdownEl = dropdownRef.value;
|
|
496
|
+
const selectorEl = selectorRef.value;
|
|
497
|
+
if (!target) return;
|
|
498
|
+
if (dropdownEl?.contains(target)) return;
|
|
499
|
+
if (selectorEl?.contains(target)) return;
|
|
500
|
+
closeDropdown();
|
|
364
501
|
};
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
if (!
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
502
|
+
const positionDropdown = (e2) => {
|
|
503
|
+
if (e2?.type === "scroll" && e2.target && dropdownRef.value?.contains(e2.target)) return;
|
|
504
|
+
if (!rootRef.value) return;
|
|
505
|
+
const rect = rootRef.value.getBoundingClientRect();
|
|
506
|
+
dropdownStyle.value = {
|
|
507
|
+
top: `${rect.bottom + window.scrollY + 8}px`,
|
|
508
|
+
left: `${rect.left + window.scrollX}px`,
|
|
509
|
+
width: `${rect.width}px`
|
|
510
|
+
};
|
|
373
511
|
};
|
|
374
|
-
const
|
|
375
|
-
const el = e2.target;
|
|
376
|
-
if (!el) return;
|
|
377
|
-
const newDigits = extractDigits(el.value, maxDigits.value);
|
|
378
|
-
showValidationHint.value = false;
|
|
379
|
-
if (validationTimer.value) {
|
|
380
|
-
clearTimeout(validationTimer.value);
|
|
381
|
-
}
|
|
382
|
-
digits.value = newDigits;
|
|
383
|
-
updateDisplay2();
|
|
384
|
-
if (newDigits.length > 0) {
|
|
385
|
-
validationTimer.value = setTimeout(() => {
|
|
386
|
-
showValidationHint.value = true;
|
|
387
|
-
}, 500);
|
|
388
|
-
}
|
|
512
|
+
const scrollFocusedIntoView = () => {
|
|
389
513
|
vue.nextTick(() => {
|
|
390
|
-
|
|
514
|
+
const list = dropdownRef.value?.lastElementChild;
|
|
515
|
+
const option = list?.children[focusedIndex.value];
|
|
516
|
+
if (!list || !option) return;
|
|
517
|
+
const listRect = list.getBoundingClientRect();
|
|
518
|
+
const optionRect = option.getBoundingClientRect();
|
|
519
|
+
let scrollAmount = 0;
|
|
520
|
+
if (optionRect.top < listRect.top) {
|
|
521
|
+
scrollAmount = list.scrollTop - (listRect.top - optionRect.top);
|
|
522
|
+
} else if (optionRect.bottom > listRect.bottom) {
|
|
523
|
+
scrollAmount = list.scrollTop + (optionRect.bottom - listRect.bottom);
|
|
524
|
+
} else {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
list.scrollTo({ top: scrollAmount, behavior: "smooth" });
|
|
391
528
|
});
|
|
392
529
|
};
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
if (!el) return;
|
|
396
|
-
if (e2.ctrlKey || e2.metaKey || e2.altKey || NavigationKeys.includes(e2.key)) return;
|
|
397
|
-
const [selStart, selEnd] = getSelection(el);
|
|
398
|
-
if (e2.key === "Backspace") {
|
|
530
|
+
const handleSearchKeydown = (e2) => {
|
|
531
|
+
if (e2.key === "ArrowDown") {
|
|
399
532
|
e2.preventDefault();
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const [start, end] = range;
|
|
404
|
-
removeDigitsRange(start, end);
|
|
405
|
-
updateDisplay2();
|
|
406
|
-
vue.nextTick(() => setCaretToDigitPosition(start));
|
|
407
|
-
}
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
if (selStart > 0) {
|
|
411
|
-
const displayStr = displayValue.value;
|
|
412
|
-
let prevPos = selStart - 1;
|
|
413
|
-
while (prevPos >= 0 && Delimiters.includes(displayStr[prevPos])) {
|
|
414
|
-
prevPos--;
|
|
415
|
-
}
|
|
416
|
-
if (prevPos >= 0) {
|
|
417
|
-
const range = formatter.value.getDigitRange(digits.value, prevPos, prevPos + 1);
|
|
418
|
-
if (range) {
|
|
419
|
-
const [start] = range;
|
|
420
|
-
removeDigitsRange(start, start + 1);
|
|
421
|
-
updateDisplay2();
|
|
422
|
-
vue.nextTick(() => setCaretToDigitPosition(start));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (e2.key === "Delete") {
|
|
533
|
+
setFocusedIndex(Math.min(focusedIndex.value + 1, filteredCountries.value.length - 1));
|
|
534
|
+
scrollFocusedIntoView();
|
|
535
|
+
} else if (e2.key === "ArrowUp") {
|
|
429
536
|
e2.preventDefault();
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const [start, end] = range;
|
|
434
|
-
removeDigitsRange(start, end);
|
|
435
|
-
updateDisplay2();
|
|
436
|
-
vue.nextTick(() => setCaretToDigitPosition(start));
|
|
437
|
-
}
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
if (selStart < displayValue.value.length) {
|
|
441
|
-
const range = formatter.value.getDigitRange(digits.value, selStart, selStart + 1);
|
|
442
|
-
if (range) {
|
|
443
|
-
const [start] = range;
|
|
444
|
-
removeDigitsRange(start, start + 1);
|
|
445
|
-
updateDisplay2();
|
|
446
|
-
vue.nextTick(() => setCaretToDigitPosition(start));
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (/^[0-9]$/.test(e2.key)) {
|
|
452
|
-
if (digits.value.length >= maxDigits.value) {
|
|
453
|
-
e2.preventDefault();
|
|
454
|
-
}
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
if (e2.key.length === 1) {
|
|
537
|
+
setFocusedIndex(Math.max(focusedIndex.value - 1, 0));
|
|
538
|
+
scrollFocusedIntoView();
|
|
539
|
+
} else if (e2.key === "Enter" && filteredCountries.value[focusedIndex.value]) {
|
|
458
540
|
e2.preventDefault();
|
|
541
|
+
selectCountry(filteredCountries.value[focusedIndex.value].id);
|
|
542
|
+
} else if (e2.key === "Escape") {
|
|
543
|
+
closeDropdown();
|
|
459
544
|
}
|
|
460
545
|
};
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
handleKeydownInternal(e2);
|
|
467
|
-
if (validationTimer.value) clearTimeout(validationTimer.value);
|
|
468
|
-
validationTimer.value = setTimeout(() => {
|
|
469
|
-
if (!isComplete.value && !isEmpty.value) showValidationHint.value = true;
|
|
470
|
-
}, 300);
|
|
471
|
-
};
|
|
472
|
-
const handlePaste = (e2) => {
|
|
473
|
-
e2.preventDefault();
|
|
474
|
-
const text = e2.clipboardData?.getData("text") || "";
|
|
475
|
-
const pastedDigits = extractDigits(text, maxDigits.value);
|
|
476
|
-
if (pastedDigits.length === 0) return;
|
|
477
|
-
const el = telRef.value;
|
|
478
|
-
if (!el) return;
|
|
479
|
-
const [selStart, selEnd] = getSelection(el);
|
|
480
|
-
if (selStart !== selEnd) {
|
|
481
|
-
const range2 = formatter.value.getDigitRange(digits.value, selStart, selEnd);
|
|
482
|
-
if (range2) {
|
|
483
|
-
const [start, end] = range2;
|
|
484
|
-
const left2 = digits.value.slice(0, start);
|
|
485
|
-
const right2 = digits.value.slice(end);
|
|
486
|
-
digits.value = extractDigits(left2 + pastedDigits + right2, maxDigits.value);
|
|
487
|
-
updateDisplay2();
|
|
488
|
-
vue.nextTick(() => setCaretToDigitPosition(start + pastedDigits.length));
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
const range = formatter.value.getDigitRange(digits.value, selStart, selStart);
|
|
493
|
-
const insertIndex = range ? range[0] : digits.value.length;
|
|
494
|
-
const left = digits.value.slice(0, insertIndex);
|
|
495
|
-
const right = digits.value.slice(insertIndex);
|
|
496
|
-
digits.value = extractDigits(left + pastedDigits + right, maxDigits.value);
|
|
497
|
-
updateDisplay2();
|
|
498
|
-
if (validationTimer.value) clearTimeout(validationTimer.value);
|
|
499
|
-
validationTimer.value = setTimeout(() => {
|
|
500
|
-
if (!isComplete.value && !isEmpty.value) showValidationHint.value = true;
|
|
501
|
-
}, 300);
|
|
502
|
-
vue.nextTick(() => setCaretToDigitPosition(insertIndex + pastedDigits.length));
|
|
503
|
-
};
|
|
504
|
-
const handleFocus = () => {
|
|
505
|
-
if (validationTimer.value) {
|
|
506
|
-
clearTimeout(validationTimer.value);
|
|
507
|
-
}
|
|
546
|
+
const removeListeners = () => {
|
|
547
|
+
window.removeEventListener("resize", positionDropdown);
|
|
548
|
+
window.removeEventListener("scroll", positionDropdown, true);
|
|
549
|
+
window.removeEventListener("click", onDocClick, true);
|
|
508
550
|
};
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
showValidationHint.value = false;
|
|
513
|
-
if (validationTimer.value) {
|
|
514
|
-
clearTimeout(validationTimer.value);
|
|
515
|
-
validationTimer.value = null;
|
|
551
|
+
vue.watch(hasDropdown, (dropdownExists) => {
|
|
552
|
+
if (!dropdownExists && dropdownOpen.value) {
|
|
553
|
+
closeDropdown();
|
|
516
554
|
}
|
|
517
|
-
};
|
|
518
|
-
vue.watch(
|
|
519
|
-
if (
|
|
520
|
-
|
|
555
|
+
});
|
|
556
|
+
vue.watch(dropdownOpen, (isOpen) => {
|
|
557
|
+
if (!isOpen) {
|
|
558
|
+
removeListeners();
|
|
559
|
+
return;
|
|
521
560
|
}
|
|
522
|
-
|
|
561
|
+
positionDropdown();
|
|
562
|
+
window.addEventListener("resize", positionDropdown);
|
|
563
|
+
window.addEventListener("scroll", positionDropdown, true);
|
|
564
|
+
window.addEventListener("click", onDocClick, true);
|
|
523
565
|
});
|
|
524
|
-
|
|
566
|
+
vue.onBeforeUnmount(removeListeners);
|
|
525
567
|
return {
|
|
526
568
|
// State
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
// Methods
|
|
543
|
-
updateDisplayFromDigits: updateDisplay2,
|
|
544
|
-
clear
|
|
569
|
+
dropdownOpen,
|
|
570
|
+
search,
|
|
571
|
+
focusedIndex,
|
|
572
|
+
dropdownStyle,
|
|
573
|
+
// Derived
|
|
574
|
+
filteredCountries,
|
|
575
|
+
hasDropdown,
|
|
576
|
+
// Actions
|
|
577
|
+
openDropdown,
|
|
578
|
+
closeDropdown,
|
|
579
|
+
toggleDropdown,
|
|
580
|
+
selectCountry,
|
|
581
|
+
setFocusedIndex,
|
|
582
|
+
handleSearchChange,
|
|
583
|
+
handleSearchKeydown
|
|
545
584
|
};
|
|
546
585
|
}
|
|
547
|
-
function useClipboard() {
|
|
586
|
+
function useClipboard(delay = 1800) {
|
|
548
587
|
const copied = vue.ref(false);
|
|
549
588
|
const isCopying = vue.ref(false);
|
|
550
|
-
|
|
551
|
-
const clearTimer = () => {
|
|
552
|
-
if (copyTimer) {
|
|
553
|
-
clearTimeout(copyTimer);
|
|
554
|
-
copyTimer = null;
|
|
555
|
-
}
|
|
556
|
-
};
|
|
589
|
+
const copyTimer = useTimer();
|
|
557
590
|
const copy = async (text) => {
|
|
558
591
|
if (isCopying.value) return false;
|
|
559
592
|
const trimmedText = text.trim();
|
|
@@ -562,11 +595,9 @@ var lib = (function(exports, vue) {
|
|
|
562
595
|
try {
|
|
563
596
|
await navigator.clipboard.writeText(trimmedText);
|
|
564
597
|
copied.value = true;
|
|
565
|
-
|
|
566
|
-
copyTimer = setTimeout(() => {
|
|
598
|
+
copyTimer.set(() => {
|
|
567
599
|
copied.value = false;
|
|
568
|
-
|
|
569
|
-
}, 1800);
|
|
600
|
+
}, delay);
|
|
570
601
|
return true;
|
|
571
602
|
} catch (err) {
|
|
572
603
|
console.warn("Copy failed", err);
|
|
@@ -575,24 +606,71 @@ var lib = (function(exports, vue) {
|
|
|
575
606
|
isCopying.value = false;
|
|
576
607
|
}
|
|
577
608
|
};
|
|
578
|
-
|
|
579
|
-
|
|
609
|
+
return { copied, isCopying, copy };
|
|
610
|
+
}
|
|
611
|
+
const DELAY = 1800;
|
|
612
|
+
function useCopyAction({ liveRef, fullFormatted, onCopy }) {
|
|
613
|
+
const liveTimer = useTimer();
|
|
614
|
+
const { copied, copy } = useClipboard(DELAY);
|
|
615
|
+
const copyAriaLabel = vue.computed(() => copied.value ? "Copied" : `Copy ${fullFormatted.value}`);
|
|
616
|
+
const copyButtonTitle = vue.computed(() => copied.value ? "Copied" : "Copy phone number");
|
|
617
|
+
const announceToScreenReader = (message) => {
|
|
618
|
+
if (!liveRef?.value) return;
|
|
619
|
+
liveRef.value.textContent = message;
|
|
620
|
+
liveTimer.set(() => {
|
|
621
|
+
if (liveRef.value) liveRef.value.textContent = "";
|
|
622
|
+
}, DELAY);
|
|
623
|
+
};
|
|
624
|
+
const onCopyClick = async () => {
|
|
625
|
+
const valueToCopy = fullFormatted.value.trim();
|
|
626
|
+
const success = await copy(valueToCopy);
|
|
627
|
+
if (success) {
|
|
628
|
+
onCopy?.(valueToCopy);
|
|
629
|
+
announceToScreenReader("Phone number copied to clipboard");
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
return {
|
|
633
|
+
copied,
|
|
634
|
+
copyAriaLabel,
|
|
635
|
+
copyButtonTitle,
|
|
636
|
+
onCopyClick
|
|
580
637
|
};
|
|
581
|
-
return { copied, isCopying, copy, onUnmount };
|
|
582
638
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
639
|
+
function useTheme({ theme }) {
|
|
640
|
+
const systemDark = vue.ref(false);
|
|
641
|
+
const themeClass = vue.computed(() => {
|
|
642
|
+
return vue.toValue(theme) !== "auto" ? `theme-${vue.toValue(theme)}` : systemDark.value ? "theme-dark" : "theme-light";
|
|
643
|
+
});
|
|
644
|
+
let mq = null;
|
|
645
|
+
const handler = (e2) => {
|
|
646
|
+
systemDark.value = e2.matches;
|
|
647
|
+
};
|
|
648
|
+
vue.onBeforeMount(() => {
|
|
649
|
+
if (typeof window === "undefined") return;
|
|
650
|
+
mq = window.matchMedia?.("(prefers-color-scheme: dark)") ?? null;
|
|
651
|
+
if (!mq) return;
|
|
652
|
+
systemDark.value = mq.matches;
|
|
653
|
+
mq.addEventListener("change", handler);
|
|
654
|
+
});
|
|
655
|
+
vue.onBeforeUnmount(() => {
|
|
656
|
+
mq?.removeEventListener("change", handler);
|
|
657
|
+
});
|
|
658
|
+
return {
|
|
659
|
+
themeClass
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
const _hoisted_1 = ["disabled", "tabindex", "aria-label", "aria-expanded", "aria-haspopup"];
|
|
663
|
+
const _hoisted_2 = ["aria-label"];
|
|
664
|
+
const _hoisted_3 = { class: "pi-code" };
|
|
665
|
+
const _hoisted_4 = { class: "pi-input-wrap" };
|
|
666
|
+
const _hoisted_5 = ["placeholder", "value", "disabled", "readonly", "aria-invalid"];
|
|
667
|
+
const _hoisted_6 = {
|
|
590
668
|
class: "pi-actions",
|
|
591
669
|
role: "toolbar",
|
|
592
670
|
"aria-label": "Phone input actions"
|
|
593
671
|
};
|
|
594
|
-
const
|
|
595
|
-
const
|
|
672
|
+
const _hoisted_7 = ["aria-label", "title"];
|
|
673
|
+
const _hoisted_8 = {
|
|
596
674
|
key: 1,
|
|
597
675
|
width: "16",
|
|
598
676
|
height: "16",
|
|
@@ -600,7 +678,7 @@ var lib = (function(exports, vue) {
|
|
|
600
678
|
fill: "none",
|
|
601
679
|
"aria-hidden": "true"
|
|
602
680
|
};
|
|
603
|
-
const
|
|
681
|
+
const _hoisted_9 = {
|
|
604
682
|
key: 2,
|
|
605
683
|
width: "16",
|
|
606
684
|
height: "16",
|
|
@@ -608,8 +686,8 @@ var lib = (function(exports, vue) {
|
|
|
608
686
|
fill: "none",
|
|
609
687
|
"aria-hidden": "true"
|
|
610
688
|
};
|
|
611
|
-
const
|
|
612
|
-
const
|
|
689
|
+
const _hoisted_10 = ["aria-label", "title"];
|
|
690
|
+
const _hoisted_11 = {
|
|
613
691
|
key: 0,
|
|
614
692
|
width: "11",
|
|
615
693
|
height: "11",
|
|
@@ -617,14 +695,14 @@ var lib = (function(exports, vue) {
|
|
|
617
695
|
fill: "none",
|
|
618
696
|
"aria-hidden": "true"
|
|
619
697
|
};
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
const
|
|
624
|
-
const
|
|
625
|
-
const
|
|
626
|
-
const
|
|
627
|
-
const
|
|
698
|
+
const _hoisted_12 = { class: "pi-search-wrap" };
|
|
699
|
+
const _hoisted_13 = ["value", "placeholder"];
|
|
700
|
+
const _hoisted_14 = ["aria-activedescendant"];
|
|
701
|
+
const _hoisted_15 = ["id", "aria-selected", "title", "onClick", "onMouseenter"];
|
|
702
|
+
const _hoisted_16 = ["aria-label"];
|
|
703
|
+
const _hoisted_17 = { class: "pi-opt-name" };
|
|
704
|
+
const _hoisted_18 = { class: "pi-opt-code" };
|
|
705
|
+
const _hoisted_19 = {
|
|
628
706
|
key: 0,
|
|
629
707
|
class: "pi-empty"
|
|
630
708
|
};
|
|
@@ -647,238 +725,133 @@ var lib = (function(exports, vue) {
|
|
|
647
725
|
dropdownClass: {},
|
|
648
726
|
disableDefaultStyles: { type: Boolean, default: false }
|
|
649
727
|
}, {
|
|
650
|
-
"modelValue": {},
|
|
728
|
+
"modelValue": { default: "" },
|
|
651
729
|
"modelModifiers": {}
|
|
652
730
|
}),
|
|
653
731
|
emits: /* @__PURE__ */ vue.mergeModels(["change", "country-change", "validation-change", "focus", "blur", "copy", "clear"], ["update:modelValue"]),
|
|
654
732
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
655
733
|
const props = __props;
|
|
656
734
|
const slots = vue.useSlots();
|
|
657
|
-
const model = vue.useModel(__props, "modelValue");
|
|
658
735
|
const emit = __emit;
|
|
736
|
+
const model = vue.useModel(__props, "modelValue");
|
|
737
|
+
const onChange = (v) => {
|
|
738
|
+
model.value = v;
|
|
739
|
+
};
|
|
740
|
+
const { country, setCountry, locale } = useCountry({
|
|
741
|
+
country: () => props.country,
|
|
742
|
+
locale: () => props.locale,
|
|
743
|
+
detect: () => props.detect,
|
|
744
|
+
onCountryChange: (c) => emit("country-change", c)
|
|
745
|
+
});
|
|
746
|
+
const {
|
|
747
|
+
digits,
|
|
748
|
+
formatter,
|
|
749
|
+
displayPlaceholder,
|
|
750
|
+
displayValue,
|
|
751
|
+
full,
|
|
752
|
+
fullFormatted,
|
|
753
|
+
isComplete,
|
|
754
|
+
isEmpty,
|
|
755
|
+
shouldShowWarn
|
|
756
|
+
} = useFormatter({
|
|
757
|
+
country,
|
|
758
|
+
value: model,
|
|
759
|
+
onChange,
|
|
760
|
+
onPhoneChange: (data) => emit("change", data),
|
|
761
|
+
onValidationChange: (complete) => emit("validation-change", complete)
|
|
762
|
+
});
|
|
763
|
+
const { showValidationHint, clearValidationHint, scheduleValidationHint } = useValidationHint();
|
|
659
764
|
const rootRef = vue.useTemplateRef("rootRef");
|
|
660
765
|
const telRef = vue.useTemplateRef("telRef");
|
|
661
|
-
const searchRef = vue.useTemplateRef("searchRef");
|
|
662
766
|
const liveRef = vue.useTemplateRef("liveRef");
|
|
663
767
|
const dropdownRef = vue.useTemplateRef("dropdownRef");
|
|
664
|
-
const
|
|
665
|
-
|
|
768
|
+
const searchRef = vue.useTemplateRef("searchRef");
|
|
769
|
+
const selectorRef = vue.useTemplateRef("selectorRef");
|
|
770
|
+
const inactive = vue.computed(() => props.disabled || props.readonly);
|
|
771
|
+
const incomplete = vue.computed(() => showValidationHint.value && shouldShowWarn.value);
|
|
772
|
+
const showCopyButton = vue.computed(() => props.showCopy && !isEmpty.value && !props.disabled);
|
|
773
|
+
const showClearButton = vue.computed(() => props.showClear && !isEmpty.value && !inactive.value);
|
|
774
|
+
const { copied, copyAriaLabel, copyButtonTitle, onCopyClick } = useCopyAction({
|
|
775
|
+
liveRef,
|
|
776
|
+
fullFormatted,
|
|
777
|
+
onCopy: (v) => emit("copy", v)
|
|
666
778
|
});
|
|
667
|
-
const
|
|
668
|
-
const countrySelector = useCountrySelector(usedLocale);
|
|
779
|
+
const focusInput = () => vue.nextTick(() => telRef.value?.focus());
|
|
669
780
|
const {
|
|
781
|
+
dropdownOpen,
|
|
670
782
|
search,
|
|
671
|
-
filteredCountries,
|
|
672
783
|
focusedIndex,
|
|
673
|
-
|
|
674
|
-
|
|
784
|
+
dropdownStyle,
|
|
785
|
+
filteredCountries,
|
|
675
786
|
hasDropdown,
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (props.theme !== "auto") return `theme-${props.theme}`;
|
|
693
|
-
if (typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
|
|
694
|
-
return "theme-dark";
|
|
695
|
-
}
|
|
696
|
-
return "theme-light";
|
|
787
|
+
closeDropdown,
|
|
788
|
+
toggleDropdown,
|
|
789
|
+
selectCountry,
|
|
790
|
+
setFocusedIndex,
|
|
791
|
+
handleSearchChange,
|
|
792
|
+
handleSearchKeydown
|
|
793
|
+
} = useCountrySelector({
|
|
794
|
+
rootRef,
|
|
795
|
+
dropdownRef,
|
|
796
|
+
searchRef,
|
|
797
|
+
selectorRef,
|
|
798
|
+
locale,
|
|
799
|
+
countryOption: () => props.country,
|
|
800
|
+
inactive,
|
|
801
|
+
onSelectCountry: setCountry,
|
|
802
|
+
onAfterSelect: focusInput
|
|
697
803
|
});
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
]);
|
|
710
|
-
const rootStyles = vue.computed(() => ({
|
|
711
|
-
"--pi-actions-count": +showCopyButton.value + +showClearButton.value + (slots["actions-before"] ? 1 : 0)
|
|
712
|
-
}));
|
|
713
|
-
const emitModelUpdate = () => {
|
|
714
|
-
if (model.value === digits.value) return;
|
|
715
|
-
model.value = digits.value;
|
|
716
|
-
emit("change", {
|
|
717
|
-
full: full.value,
|
|
718
|
-
fullFormatted: fullFormatted.value,
|
|
719
|
-
digits: digits.value
|
|
720
|
-
});
|
|
721
|
-
};
|
|
722
|
-
const onInput = async (e2) => {
|
|
723
|
-
if (inactive.value) return;
|
|
724
|
-
mask.handleInput(e2);
|
|
725
|
-
await vue.nextTick();
|
|
726
|
-
emitModelUpdate();
|
|
727
|
-
};
|
|
728
|
-
const onKeydown = async (e2) => {
|
|
729
|
-
if (inactive.value) return;
|
|
730
|
-
mask.handleKeydown(e2);
|
|
731
|
-
await vue.nextTick();
|
|
732
|
-
emitModelUpdate();
|
|
733
|
-
};
|
|
734
|
-
const onPaste = async (e2) => {
|
|
735
|
-
if (inactive.value) return;
|
|
736
|
-
mask.handlePaste(e2);
|
|
737
|
-
await vue.nextTick();
|
|
738
|
-
emitModelUpdate();
|
|
739
|
-
};
|
|
740
|
-
const onFocus = (e2) => {
|
|
741
|
-
mask.handleFocus();
|
|
742
|
-
dropdownOpened.value = false;
|
|
804
|
+
const { handleBeforeInput, handleInput, handleKeydown, handlePaste } = useInputHandlers({
|
|
805
|
+
formatter,
|
|
806
|
+
digits,
|
|
807
|
+
inactive,
|
|
808
|
+
onChange,
|
|
809
|
+
scheduleValidationHint
|
|
810
|
+
});
|
|
811
|
+
const handleFocus = (e2) => {
|
|
812
|
+
clearValidationHint(false);
|
|
813
|
+
closeDropdown();
|
|
743
814
|
emit("focus", e2);
|
|
744
815
|
};
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
await vue.nextTick();
|
|
750
|
-
telRef.value?.focus();
|
|
751
|
-
};
|
|
752
|
-
const onCopyClick = async () => {
|
|
753
|
-
const valueToCopy = fullFormatted.value;
|
|
754
|
-
const success = await copy(valueToCopy);
|
|
755
|
-
if (success) {
|
|
756
|
-
emit("copy", valueToCopy);
|
|
757
|
-
}
|
|
758
|
-
};
|
|
759
|
-
const onClearClick = async () => {
|
|
760
|
-
mask.clear();
|
|
761
|
-
model.value = "";
|
|
762
|
-
emit("change", {
|
|
763
|
-
full: "",
|
|
764
|
-
fullFormatted: "",
|
|
765
|
-
digits: ""
|
|
766
|
-
});
|
|
816
|
+
const handleBlur = (e2) => emit("blur", e2);
|
|
817
|
+
const clear = () => {
|
|
818
|
+
onChange("");
|
|
819
|
+
clearValidationHint();
|
|
767
820
|
emit("clear");
|
|
768
|
-
await vue.nextTick();
|
|
769
|
-
telRef.value?.focus();
|
|
770
|
-
};
|
|
771
|
-
const positionDropdown = (e2) => {
|
|
772
|
-
if (e2?.type === "scroll" && e2.target && dropdownRef.value?.contains(e2.target)) return;
|
|
773
|
-
const root = rootRef.value;
|
|
774
|
-
if (!root) return;
|
|
775
|
-
const rect = root.getBoundingClientRect();
|
|
776
|
-
dropdownStyle.value = {
|
|
777
|
-
top: `${rect.bottom + window.scrollY + 8}px`,
|
|
778
|
-
left: `${rect.left + window.scrollX}px`,
|
|
779
|
-
width: `${rect.width}px`
|
|
780
|
-
};
|
|
781
821
|
};
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
window.removeEventListener("resize", positionDropdown);
|
|
822
|
+
const onClearClick = () => {
|
|
823
|
+
clear();
|
|
824
|
+
focusInput();
|
|
786
825
|
};
|
|
787
|
-
const toggleDropdown = async () => {
|
|
788
|
-
if (inactive.value || !hasDropdown.value) return;
|
|
789
|
-
await countrySelector.toggleDropdown(searchRef);
|
|
790
|
-
if (dropdownOpened.value) {
|
|
791
|
-
positionDropdown();
|
|
792
|
-
window.addEventListener("scroll", positionDropdown, true);
|
|
793
|
-
window.addEventListener("click", onDocClick, true);
|
|
794
|
-
window.addEventListener("resize", positionDropdown);
|
|
795
|
-
} else {
|
|
796
|
-
removeDropdownListeners();
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
const scrollFocusedIntoView = async () => {
|
|
800
|
-
await vue.nextTick();
|
|
801
|
-
const list = dropdownRef.value?.lastElementChild;
|
|
802
|
-
if (!list) return;
|
|
803
|
-
const option = list.children[focusedIndex.value];
|
|
804
|
-
if (!option) return;
|
|
805
|
-
const listRect = list.getBoundingClientRect();
|
|
806
|
-
const optionRect = option.getBoundingClientRect();
|
|
807
|
-
let scrollAmount = 0;
|
|
808
|
-
if (optionRect.top < listRect.top) {
|
|
809
|
-
scrollAmount = list.scrollTop - (listRect.top - optionRect.top);
|
|
810
|
-
} else if (optionRect.bottom > listRect.bottom) {
|
|
811
|
-
scrollAmount = list.scrollTop + (optionRect.bottom - listRect.bottom);
|
|
812
|
-
} else {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
list.scrollTo({ top: scrollAmount, behavior: "smooth" });
|
|
816
|
-
};
|
|
817
|
-
const onDocClick = (ev) => {
|
|
818
|
-
const dropdown = dropdownRef.value;
|
|
819
|
-
const selector = rootRef.value?.firstChild;
|
|
820
|
-
if (!(dropdown || selector)) return;
|
|
821
|
-
const target = ev.target;
|
|
822
|
-
if (!target || dropdown?.contains(target) || selector?.contains(target)) return;
|
|
823
|
-
dropdownOpened.value = false;
|
|
824
|
-
};
|
|
825
|
-
vue.watch(
|
|
826
|
-
model,
|
|
827
|
-
(newValue) => {
|
|
828
|
-
if (!newValue) {
|
|
829
|
-
if (!isEmpty.value) mask.clear();
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
const currentDigits = digits.value;
|
|
833
|
-
if (newValue !== currentDigits) {
|
|
834
|
-
const incomingDigits = newValue.replace(/\D/g, "");
|
|
835
|
-
if (incomingDigits !== currentDigits) {
|
|
836
|
-
digits.value = incomingDigits;
|
|
837
|
-
mask.updateDisplayFromDigits();
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
},
|
|
841
|
-
{ immediate: true }
|
|
842
|
-
);
|
|
843
|
-
vue.watch(
|
|
844
|
-
[() => props.country, () => props.detect],
|
|
845
|
-
async ([country, detect]) => {
|
|
846
|
-
await vue.nextTick();
|
|
847
|
-
await countrySelector.initCountry(country, detect, () => emit("country-change", selected.value));
|
|
848
|
-
},
|
|
849
|
-
{ immediate: true }
|
|
850
|
-
);
|
|
851
|
-
vue.watch(
|
|
852
|
-
copyMessage,
|
|
853
|
-
(val) => {
|
|
854
|
-
if (liveRef.value && val) {
|
|
855
|
-
liveRef.value.textContent = val;
|
|
856
|
-
}
|
|
857
|
-
},
|
|
858
|
-
{ flush: "post" }
|
|
859
|
-
);
|
|
860
|
-
vue.watch(
|
|
861
|
-
isComplete,
|
|
862
|
-
(valid) => {
|
|
863
|
-
emit("validation-change", valid);
|
|
864
|
-
},
|
|
865
|
-
{ flush: "post" }
|
|
866
|
-
);
|
|
867
|
-
vue.onBeforeUnmount(() => {
|
|
868
|
-
removeDropdownListeners();
|
|
869
|
-
onClipboardUnmount();
|
|
870
|
-
});
|
|
871
826
|
__expose({
|
|
872
|
-
focus:
|
|
827
|
+
focus: focusInput,
|
|
873
828
|
blur: () => telRef.value?.blur(),
|
|
874
|
-
clear
|
|
875
|
-
selectCountry
|
|
829
|
+
clear,
|
|
830
|
+
selectCountry,
|
|
876
831
|
getFullNumber: () => full.value,
|
|
877
832
|
getFullFormattedNumber: () => fullFormatted.value,
|
|
878
833
|
getDigits: () => digits.value,
|
|
879
834
|
isValid: () => isComplete.value,
|
|
880
835
|
isComplete: () => isComplete.value
|
|
881
836
|
});
|
|
837
|
+
const { themeClass } = useTheme({
|
|
838
|
+
theme: () => props.theme
|
|
839
|
+
});
|
|
840
|
+
const rootClasses = vue.computed(() => [
|
|
841
|
+
"phone-input",
|
|
842
|
+
`size-${props.size}`,
|
|
843
|
+
themeClass.value,
|
|
844
|
+
{
|
|
845
|
+
"is-disabled": props.disabled,
|
|
846
|
+
"is-readonly": props.readonly,
|
|
847
|
+
"is-unstyled": props.disableDefaultStyles,
|
|
848
|
+
"is-incomplete": props.withValidity && incomplete.value,
|
|
849
|
+
"is-complete": props.withValidity && isComplete.value
|
|
850
|
+
}
|
|
851
|
+
]);
|
|
852
|
+
const rootStyles = vue.computed(() => ({
|
|
853
|
+
"--pi-actions-count": +showCopyButton.value + +showClearButton.value + (slots["actions-before"] ? 1 : 0)
|
|
854
|
+
}));
|
|
882
855
|
return (_ctx, _cache) => {
|
|
883
856
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
884
857
|
ref_key: "rootRef",
|
|
@@ -888,36 +861,41 @@ var lib = (function(exports, vue) {
|
|
|
888
861
|
class: vue.normalizeClass(rootClasses.value),
|
|
889
862
|
style: vue.normalizeStyle(rootStyles.value)
|
|
890
863
|
}, [
|
|
891
|
-
vue.createElementVNode("div",
|
|
864
|
+
vue.createElementVNode("div", {
|
|
865
|
+
ref_key: "selectorRef",
|
|
866
|
+
ref: selectorRef,
|
|
867
|
+
class: "pi-selector"
|
|
868
|
+
}, [
|
|
892
869
|
vue.createElementVNode("button", {
|
|
893
870
|
type: "button",
|
|
894
871
|
class: vue.normalizeClass(["pi-selector-btn", { "no-dropdown": !vue.unref(hasDropdown) || __props.readonly }]),
|
|
895
872
|
disabled: __props.disabled,
|
|
896
873
|
tabindex: inactive.value || !vue.unref(hasDropdown) ? -1 : void 0,
|
|
897
|
-
"aria-label": `Selected country: ${vue.unref(
|
|
898
|
-
"aria-expanded": vue.unref(
|
|
874
|
+
"aria-label": `Selected country: ${vue.unref(country).name}`,
|
|
875
|
+
"aria-expanded": vue.unref(dropdownOpen),
|
|
899
876
|
"aria-haspopup": vue.unref(hasDropdown) ? "listbox" : void 0,
|
|
900
|
-
onClick:
|
|
877
|
+
onClick: _cache[0] || (_cache[0] = //@ts-ignore
|
|
878
|
+
(...args) => vue.unref(toggleDropdown) && vue.unref(toggleDropdown)(...args))
|
|
901
879
|
}, [
|
|
902
880
|
vue.createElementVNode("span", {
|
|
903
881
|
class: "pi-flag",
|
|
904
882
|
role: "img",
|
|
905
|
-
"aria-label": `${vue.unref(
|
|
883
|
+
"aria-label": `${vue.unref(country).name} flag`
|
|
906
884
|
}, [
|
|
907
|
-
vue.renderSlot(_ctx.$slots, "flag", { country: vue.unref(
|
|
908
|
-
vue.createTextVNode(vue.toDisplayString(vue.unref(
|
|
885
|
+
vue.renderSlot(_ctx.$slots, "flag", { country: vue.unref(country) }, () => [
|
|
886
|
+
vue.createTextVNode(vue.toDisplayString(vue.unref(country).flag), 1)
|
|
909
887
|
], true)
|
|
910
|
-
], 8,
|
|
911
|
-
vue.createElementVNode("span",
|
|
888
|
+
], 8, _hoisted_2),
|
|
889
|
+
vue.createElementVNode("span", _hoisted_3, vue.toDisplayString(vue.unref(country).code), 1),
|
|
912
890
|
!inactive.value && vue.unref(hasDropdown) ? (vue.openBlock(), vue.createElementBlock("svg", {
|
|
913
891
|
key: 0,
|
|
914
|
-
class: vue.normalizeClass(["pi-chevron", { "is-open": vue.unref(
|
|
892
|
+
class: vue.normalizeClass(["pi-chevron", { "is-open": vue.unref(dropdownOpen) }]),
|
|
915
893
|
width: "12",
|
|
916
894
|
height: "12",
|
|
917
895
|
viewBox: "0 0 12 12",
|
|
918
896
|
fill: "none",
|
|
919
897
|
"aria-hidden": "true"
|
|
920
|
-
}, [..._cache[
|
|
898
|
+
}, [..._cache[8] || (_cache[8] = [
|
|
921
899
|
vue.createElementVNode("path", {
|
|
922
900
|
d: "M2.5 4.5L6 8L9.5 4.5",
|
|
923
901
|
stroke: "currentColor",
|
|
@@ -926,9 +904,9 @@ var lib = (function(exports, vue) {
|
|
|
926
904
|
"stroke-linejoin": "round"
|
|
927
905
|
}, null, -1)
|
|
928
906
|
])], 2)) : vue.createCommentVNode("", true)
|
|
929
|
-
], 10,
|
|
930
|
-
]),
|
|
931
|
-
vue.createElementVNode("div",
|
|
907
|
+
], 10, _hoisted_1)
|
|
908
|
+
], 512),
|
|
909
|
+
vue.createElementVNode("div", _hoisted_4, [
|
|
932
910
|
vue.createElementVNode("input", {
|
|
933
911
|
ref_key: "telRef",
|
|
934
912
|
ref: telRef,
|
|
@@ -943,16 +921,19 @@ var lib = (function(exports, vue) {
|
|
|
943
921
|
value: vue.unref(displayValue),
|
|
944
922
|
disabled: __props.disabled,
|
|
945
923
|
readonly: __props.readonly,
|
|
946
|
-
"aria-invalid":
|
|
947
|
-
onBeforeinput: _cache[
|
|
948
|
-
(...args) => vue.unref(
|
|
949
|
-
onInput
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
924
|
+
"aria-invalid": incomplete.value,
|
|
925
|
+
onBeforeinput: _cache[1] || (_cache[1] = //@ts-ignore
|
|
926
|
+
(...args) => vue.unref(handleBeforeInput) && vue.unref(handleBeforeInput)(...args)),
|
|
927
|
+
onInput: _cache[2] || (_cache[2] = //@ts-ignore
|
|
928
|
+
(...args) => vue.unref(handleInput) && vue.unref(handleInput)(...args)),
|
|
929
|
+
onKeydown: _cache[3] || (_cache[3] = //@ts-ignore
|
|
930
|
+
(...args) => vue.unref(handleKeydown) && vue.unref(handleKeydown)(...args)),
|
|
931
|
+
onPaste: _cache[4] || (_cache[4] = //@ts-ignore
|
|
932
|
+
(...args) => vue.unref(handlePaste) && vue.unref(handlePaste)(...args)),
|
|
933
|
+
onFocus: handleFocus,
|
|
934
|
+
onBlur: handleBlur
|
|
935
|
+
}, null, 40, _hoisted_5),
|
|
936
|
+
vue.createElementVNode("div", _hoisted_6, [
|
|
956
937
|
vue.createVNode(vue.Transition, { name: "fade-scale" }, {
|
|
957
938
|
default: vue.withCtx(() => [
|
|
958
939
|
vue.renderSlot(_ctx.$slots, "actions-before", {}, void 0, true)
|
|
@@ -964,26 +945,27 @@ var lib = (function(exports, vue) {
|
|
|
964
945
|
showCopyButton.value ? (vue.openBlock(), vue.createElementBlock("button", {
|
|
965
946
|
key: 0,
|
|
966
947
|
type: "button",
|
|
967
|
-
class: vue.normalizeClass(["pi-btn", { "is-copied": vue.unref(copied) }]),
|
|
968
|
-
"aria-label": copyAriaLabel
|
|
969
|
-
title: copyButtonTitle
|
|
970
|
-
onClick:
|
|
948
|
+
class: vue.normalizeClass(["pi-btn", "pi-btn-copy", { "is-copied": vue.unref(copied) }]),
|
|
949
|
+
"aria-label": vue.unref(copyAriaLabel),
|
|
950
|
+
title: vue.unref(copyButtonTitle),
|
|
951
|
+
onClick: _cache[5] || (_cache[5] = //@ts-ignore
|
|
952
|
+
(...args) => vue.unref(onCopyClick) && vue.unref(onCopyClick)(...args))
|
|
971
953
|
}, [
|
|
972
954
|
slots["copy-svg"] ? vue.renderSlot(_ctx.$slots, "copy-svg", {
|
|
973
955
|
key: 0,
|
|
974
956
|
copied: vue.unref(copied)
|
|
975
|
-
}, void 0, true) : !vue.unref(copied) ? (vue.openBlock(), vue.createElementBlock("svg",
|
|
957
|
+
}, void 0, true) : !vue.unref(copied) ? (vue.openBlock(), vue.createElementBlock("svg", _hoisted_8, [..._cache[9] || (_cache[9] = [
|
|
976
958
|
vue.createElementVNode("path", {
|
|
977
959
|
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",
|
|
978
960
|
fill: "currentColor"
|
|
979
961
|
}, null, -1)
|
|
980
|
-
])])) : (vue.openBlock(), vue.createElementBlock("svg",
|
|
962
|
+
])])) : (vue.openBlock(), vue.createElementBlock("svg", _hoisted_9, [..._cache[10] || (_cache[10] = [
|
|
981
963
|
vue.createElementVNode("path", {
|
|
982
964
|
d: "M6.5 11.5L3 8L4.06 6.94L6.5 9.38L11.94 3.94L13 5L6.5 11.5Z",
|
|
983
965
|
fill: "currentColor"
|
|
984
966
|
}, null, -1)
|
|
985
967
|
])]))
|
|
986
|
-
], 10,
|
|
968
|
+
], 10, _hoisted_7)) : vue.createCommentVNode("", true)
|
|
987
969
|
]),
|
|
988
970
|
_: 3
|
|
989
971
|
}),
|
|
@@ -992,18 +974,18 @@ var lib = (function(exports, vue) {
|
|
|
992
974
|
showClearButton.value ? (vue.openBlock(), vue.createElementBlock("button", {
|
|
993
975
|
key: 0,
|
|
994
976
|
type: "button",
|
|
995
|
-
class: "pi-btn",
|
|
977
|
+
class: "pi-btn pi-btn-clear",
|
|
996
978
|
"aria-label": __props.clearButtonLabel,
|
|
997
979
|
title: __props.clearButtonLabel,
|
|
998
980
|
onClick: onClearClick
|
|
999
981
|
}, [
|
|
1000
|
-
!slots["clear-svg"] ? (vue.openBlock(), vue.createElementBlock("svg",
|
|
982
|
+
!slots["clear-svg"] ? (vue.openBlock(), vue.createElementBlock("svg", _hoisted_11, [..._cache[11] || (_cache[11] = [
|
|
1001
983
|
vue.createElementVNode("path", {
|
|
1002
984
|
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",
|
|
1003
985
|
fill: "currentColor"
|
|
1004
986
|
}, null, -1)
|
|
1005
987
|
])])) : vue.renderSlot(_ctx.$slots, "clear-svg", { key: 1 }, void 0, true)
|
|
1006
|
-
], 8,
|
|
988
|
+
], 8, _hoisted_10)) : vue.createCommentVNode("", true)
|
|
1007
989
|
]),
|
|
1008
990
|
_: 3
|
|
1009
991
|
})
|
|
@@ -1012,42 +994,30 @@ var lib = (function(exports, vue) {
|
|
|
1012
994
|
(vue.openBlock(), vue.createBlock(vue.Teleport, { to: "body" }, [
|
|
1013
995
|
vue.createVNode(vue.Transition, { name: "dropdown" }, {
|
|
1014
996
|
default: vue.withCtx(() => [
|
|
1015
|
-
vue.unref(
|
|
997
|
+
vue.unref(dropdownOpen) ? (vue.openBlock(), vue.createElementBlock("div", {
|
|
1016
998
|
key: 0,
|
|
1017
999
|
ref_key: "dropdownRef",
|
|
1018
1000
|
ref: dropdownRef,
|
|
1019
|
-
class: vue.normalizeClass(["phone-dropdown", [__props.dropdownClass, themeClass
|
|
1001
|
+
class: vue.normalizeClass(["phone-dropdown", [__props.dropdownClass, vue.unref(themeClass)]]),
|
|
1020
1002
|
role: "dialog",
|
|
1021
1003
|
"aria-modal": "false",
|
|
1022
1004
|
"aria-label": "Select country",
|
|
1023
|
-
style: vue.normalizeStyle(dropdownStyle
|
|
1005
|
+
style: vue.normalizeStyle(vue.unref(dropdownStyle))
|
|
1024
1006
|
}, [
|
|
1025
|
-
vue.createElementVNode("div",
|
|
1026
|
-
vue.
|
|
1007
|
+
vue.createElementVNode("div", _hoisted_12, [
|
|
1008
|
+
vue.createElementVNode("input", {
|
|
1027
1009
|
ref_key: "searchRef",
|
|
1028
1010
|
ref: searchRef,
|
|
1029
|
-
|
|
1011
|
+
value: vue.unref(search),
|
|
1030
1012
|
type: "search",
|
|
1031
1013
|
class: "pi-search",
|
|
1032
1014
|
"aria-label": "Search countries",
|
|
1033
1015
|
placeholder: __props.searchPlaceholder,
|
|
1034
|
-
onKeydown: [
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
(...args) => vue.unref(chooseFocusedOption) && vue.unref(chooseFocusedOption)(...args),
|
|
1040
|
-
["prevent"]
|
|
1041
|
-
), ["enter"])),
|
|
1042
|
-
_cache[5] || (_cache[5] = vue.withKeys(
|
|
1043
|
-
//@ts-ignore
|
|
1044
|
-
(...args) => vue.unref(closeDropdown) && vue.unref(closeDropdown)(...args),
|
|
1045
|
-
["escape"]
|
|
1046
|
-
))
|
|
1047
|
-
]
|
|
1048
|
-
}, null, 40, _hoisted_14), [
|
|
1049
|
-
[vue.vModelText, vue.unref(search)]
|
|
1050
|
-
])
|
|
1016
|
+
onKeydown: _cache[6] || (_cache[6] = //@ts-ignore
|
|
1017
|
+
(...args) => vue.unref(handleSearchKeydown) && vue.unref(handleSearchKeydown)(...args)),
|
|
1018
|
+
onInput: _cache[7] || (_cache[7] = //@ts-ignore
|
|
1019
|
+
(...args) => vue.unref(handleSearchChange) && vue.unref(handleSearchChange)(...args))
|
|
1020
|
+
}, null, 40, _hoisted_13)
|
|
1051
1021
|
]),
|
|
1052
1022
|
vue.createElementVNode("ul", {
|
|
1053
1023
|
class: "pi-options",
|
|
@@ -1064,13 +1034,13 @@ var lib = (function(exports, vue) {
|
|
|
1064
1034
|
"pi-option",
|
|
1065
1035
|
{
|
|
1066
1036
|
"is-focused": idx === vue.unref(focusedIndex),
|
|
1067
|
-
"is-selected": c.id === vue.unref(
|
|
1037
|
+
"is-selected": c.id === vue.unref(country).id
|
|
1068
1038
|
}
|
|
1069
1039
|
]),
|
|
1070
|
-
"aria-selected": c.id === vue.unref(
|
|
1040
|
+
"aria-selected": c.id === vue.unref(country).id,
|
|
1071
1041
|
title: c.name,
|
|
1072
|
-
onClick: ($event) =>
|
|
1073
|
-
onMouseenter: ($event) =>
|
|
1042
|
+
onClick: ($event) => vue.unref(selectCountry)(c.id),
|
|
1043
|
+
onMouseenter: ($event) => vue.unref(setFocusedIndex)(idx)
|
|
1074
1044
|
}, [
|
|
1075
1045
|
vue.createElementVNode("span", {
|
|
1076
1046
|
class: "pi-flag",
|
|
@@ -1080,13 +1050,13 @@ var lib = (function(exports, vue) {
|
|
|
1080
1050
|
vue.renderSlot(_ctx.$slots, "flag", { country: c }, () => [
|
|
1081
1051
|
vue.createTextVNode(vue.toDisplayString(c.flag), 1)
|
|
1082
1052
|
], true)
|
|
1083
|
-
], 8,
|
|
1084
|
-
vue.createElementVNode("span",
|
|
1085
|
-
vue.createElementVNode("span",
|
|
1086
|
-
], 42,
|
|
1053
|
+
], 8, _hoisted_16),
|
|
1054
|
+
vue.createElementVNode("span", _hoisted_17, vue.toDisplayString(c.name), 1),
|
|
1055
|
+
vue.createElementVNode("span", _hoisted_18, vue.toDisplayString(c.code), 1)
|
|
1056
|
+
], 42, _hoisted_15);
|
|
1087
1057
|
}), 128)),
|
|
1088
|
-
vue.unref(filteredCountries).length === 0 ? (vue.openBlock(), vue.createElementBlock("li",
|
|
1089
|
-
], 8,
|
|
1058
|
+
vue.unref(filteredCountries).length === 0 ? (vue.openBlock(), vue.createElementBlock("li", _hoisted_19, vue.toDisplayString(__props.noResultsText), 1)) : vue.createCommentVNode("", true)
|
|
1059
|
+
], 8, _hoisted_14)
|
|
1090
1060
|
], 6)) : vue.createCommentVNode("", true)
|
|
1091
1061
|
]),
|
|
1092
1062
|
_: 3
|
|
@@ -1111,21 +1081,7 @@ var lib = (function(exports, vue) {
|
|
|
1111
1081
|
}
|
|
1112
1082
|
return target;
|
|
1113
1083
|
};
|
|
1114
|
-
const PhoneInput = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1115
|
-
function detectCountryFromLocale() {
|
|
1116
|
-
try {
|
|
1117
|
-
const lang = getNavigatorLang();
|
|
1118
|
-
try {
|
|
1119
|
-
const loc = new Intl.Locale(lang);
|
|
1120
|
-
if (loc.region) return loc.region.toUpperCase();
|
|
1121
|
-
} catch {
|
|
1122
|
-
}
|
|
1123
|
-
const parts = lang.split(/[-_]/);
|
|
1124
|
-
if (parts.length > 1) return parts[1]?.toUpperCase() || null;
|
|
1125
|
-
} catch {
|
|
1126
|
-
}
|
|
1127
|
-
return null;
|
|
1128
|
-
}
|
|
1084
|
+
const PhoneInput = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-33134720"]]);
|
|
1129
1085
|
async function initState(binding) {
|
|
1130
1086
|
const value = binding.value;
|
|
1131
1087
|
let options = {};
|
|
@@ -1173,140 +1129,44 @@ var lib = (function(exports, vue) {
|
|
|
1173
1129
|
});
|
|
1174
1130
|
}
|
|
1175
1131
|
}
|
|
1176
|
-
function createBeforeInputHandler(el) {
|
|
1177
|
-
return (e2) => {
|
|
1178
|
-
const data = e2.data;
|
|
1179
|
-
if (e2.inputType !== "insertText" || !data) return;
|
|
1180
|
-
if (InvalidPattern.test(data) || data === " " && el.value.endsWith(" ")) {
|
|
1181
|
-
e2.preventDefault();
|
|
1182
|
-
}
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
1132
|
function createInputHandler(el, state) {
|
|
1186
1133
|
return (e2) => {
|
|
1187
|
-
const
|
|
1188
|
-
if (!
|
|
1189
|
-
|
|
1190
|
-
const maxDigits = state.formatter.getMaxDigits();
|
|
1191
|
-
state.digits = extractDigits(raw, maxDigits);
|
|
1134
|
+
const result = processInput(e2, { formatter: state.formatter });
|
|
1135
|
+
if (!result) return;
|
|
1136
|
+
state.digits = result.newDigits;
|
|
1192
1137
|
updateDisplay(el, state);
|
|
1193
1138
|
vue.nextTick(() => {
|
|
1194
|
-
const pos = state.formatter.getCaretPosition(
|
|
1139
|
+
const pos = state.formatter.getCaretPosition(result.caretDigitIndex);
|
|
1195
1140
|
setCaret(el, pos);
|
|
1196
1141
|
});
|
|
1197
1142
|
};
|
|
1198
1143
|
}
|
|
1199
1144
|
function createKeydownHandler(el, state) {
|
|
1200
1145
|
return (e2) => {
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
const pos = state.formatter.getCaretPosition(start);
|
|
1213
|
-
setCaret(el, pos);
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1216
|
-
return;
|
|
1217
|
-
}
|
|
1218
|
-
if (selStart > 0) {
|
|
1219
|
-
const displayStr = el.value;
|
|
1220
|
-
let prevPos = selStart - 1;
|
|
1221
|
-
while (prevPos >= 0 && Delimiters.includes(displayStr[prevPos])) {
|
|
1222
|
-
prevPos--;
|
|
1223
|
-
}
|
|
1224
|
-
if (prevPos >= 0) {
|
|
1225
|
-
const range = state.formatter.getDigitRange(state.digits, prevPos, prevPos + 1);
|
|
1226
|
-
if (range) {
|
|
1227
|
-
const [start] = range;
|
|
1228
|
-
state.digits = state.digits.slice(0, start) + state.digits.slice(start + 1);
|
|
1229
|
-
updateDisplay(el, state);
|
|
1230
|
-
vue.nextTick(() => {
|
|
1231
|
-
const pos = state.formatter.getCaretPosition(start);
|
|
1232
|
-
setCaret(el, pos);
|
|
1233
|
-
});
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
return;
|
|
1238
|
-
}
|
|
1239
|
-
if (e2.key === "Delete") {
|
|
1240
|
-
e2.preventDefault();
|
|
1241
|
-
if (selStart !== selEnd) {
|
|
1242
|
-
const range = state.formatter.getDigitRange(state.digits, selStart, selEnd);
|
|
1243
|
-
if (range) {
|
|
1244
|
-
const [start, end] = range;
|
|
1245
|
-
state.digits = state.digits.slice(0, start) + state.digits.slice(end);
|
|
1246
|
-
updateDisplay(el, state);
|
|
1247
|
-
vue.nextTick(() => {
|
|
1248
|
-
const pos = state.formatter.getCaretPosition(start);
|
|
1249
|
-
setCaret(el, pos);
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
if (selStart < el.value.length) {
|
|
1255
|
-
const range = state.formatter.getDigitRange(state.digits, selStart, selStart + 1);
|
|
1256
|
-
if (range) {
|
|
1257
|
-
const [start] = range;
|
|
1258
|
-
state.digits = state.digits.slice(0, start) + state.digits.slice(start + 1);
|
|
1259
|
-
updateDisplay(el, state);
|
|
1260
|
-
vue.nextTick(() => {
|
|
1261
|
-
const pos = state.formatter.getCaretPosition(start);
|
|
1262
|
-
setCaret(el, pos);
|
|
1263
|
-
});
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
if (/^[0-9]$/.test(e2.key)) {
|
|
1269
|
-
if (state.digits.length >= state.formatter.getMaxDigits()) {
|
|
1270
|
-
e2.preventDefault();
|
|
1271
|
-
}
|
|
1272
|
-
return;
|
|
1273
|
-
}
|
|
1274
|
-
if (e2.key.length === 1) {
|
|
1275
|
-
e2.preventDefault();
|
|
1276
|
-
}
|
|
1146
|
+
const result = processKeydown(e2, {
|
|
1147
|
+
digits: state.digits,
|
|
1148
|
+
formatter: state.formatter
|
|
1149
|
+
});
|
|
1150
|
+
if (!result) return;
|
|
1151
|
+
state.digits = result.newDigits;
|
|
1152
|
+
updateDisplay(el, state);
|
|
1153
|
+
vue.nextTick(() => {
|
|
1154
|
+
const pos = state.formatter.getCaretPosition(result.caretDigitIndex);
|
|
1155
|
+
setCaret(el, pos);
|
|
1156
|
+
});
|
|
1277
1157
|
};
|
|
1278
1158
|
}
|
|
1279
1159
|
function createPasteHandler(el, state) {
|
|
1280
1160
|
return (e2) => {
|
|
1281
|
-
e2
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
if (
|
|
1286
|
-
|
|
1287
|
-
if (selStart !== selEnd) {
|
|
1288
|
-
const range2 = state.formatter.getDigitRange(state.digits, selStart, selEnd);
|
|
1289
|
-
if (range2) {
|
|
1290
|
-
const [start, end] = range2;
|
|
1291
|
-
const left2 = state.digits.slice(0, start);
|
|
1292
|
-
const right2 = state.digits.slice(end);
|
|
1293
|
-
state.digits = extractDigits(left2 + pastedDigits + right2, maxDigits);
|
|
1294
|
-
updateDisplay(el, state);
|
|
1295
|
-
vue.nextTick(() => {
|
|
1296
|
-
const pos = state.formatter.getCaretPosition(start + pastedDigits.length);
|
|
1297
|
-
setCaret(el, pos);
|
|
1298
|
-
});
|
|
1299
|
-
return;
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
const range = state.formatter.getDigitRange(state.digits, selStart, selStart);
|
|
1303
|
-
const insertIndex = range ? range[0] : state.digits.length;
|
|
1304
|
-
const left = state.digits.slice(0, insertIndex);
|
|
1305
|
-
const right = state.digits.slice(insertIndex);
|
|
1306
|
-
state.digits = extractDigits(left + pastedDigits + right, maxDigits);
|
|
1161
|
+
const result = processPaste(e2, {
|
|
1162
|
+
digits: state.digits,
|
|
1163
|
+
formatter: state.formatter
|
|
1164
|
+
});
|
|
1165
|
+
if (!result) return;
|
|
1166
|
+
state.digits = result.newDigits;
|
|
1307
1167
|
updateDisplay(el, state);
|
|
1308
1168
|
vue.nextTick(() => {
|
|
1309
|
-
const pos = state.formatter.getCaretPosition(
|
|
1169
|
+
const pos = state.formatter.getCaretPosition(result.caretDigitIndex);
|
|
1310
1170
|
setCaret(el, pos);
|
|
1311
1171
|
});
|
|
1312
1172
|
};
|
|
@@ -1338,7 +1198,7 @@ var lib = (function(exports, vue) {
|
|
|
1338
1198
|
state.inputHandler = createInputHandler(el, state);
|
|
1339
1199
|
state.keydownHandler = createKeydownHandler(el, state);
|
|
1340
1200
|
state.pasteHandler = createPasteHandler(el, state);
|
|
1341
|
-
state.beforeInputHandler =
|
|
1201
|
+
state.beforeInputHandler = processBeforeInput;
|
|
1342
1202
|
el.addEventListener("beforeinput", state.beforeInputHandler);
|
|
1343
1203
|
el.addEventListener("input", state.inputHandler);
|
|
1344
1204
|
el.addEventListener("keydown", state.keydownHandler);
|
|
@@ -1369,8 +1229,10 @@ var lib = (function(exports, vue) {
|
|
|
1369
1229
|
if (newCountry && newCountry !== oldCountry) {
|
|
1370
1230
|
await updateCountry(el, state, newCountry);
|
|
1371
1231
|
}
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1232
|
+
const maxDigits = state.formatter.getMaxDigits();
|
|
1233
|
+
const newDigits = extractDigits(el.value, maxDigits);
|
|
1234
|
+
const normalizedDisplay = state.formatter.formatDisplay(newDigits);
|
|
1235
|
+
if (newDigits !== state.digits || el.value !== normalizedDisplay) {
|
|
1374
1236
|
state.digits = newDigits;
|
|
1375
1237
|
updateDisplay(el, state);
|
|
1376
1238
|
}
|
|
@@ -1385,6 +1247,82 @@ var lib = (function(exports, vue) {
|
|
|
1385
1247
|
delete el.__phoneMaskState;
|
|
1386
1248
|
}
|
|
1387
1249
|
};
|
|
1250
|
+
function usePhoneMask(options) {
|
|
1251
|
+
const inputRef = vue.shallowRef(null);
|
|
1252
|
+
const { country, setCountry } = useCountry({
|
|
1253
|
+
country: options.country,
|
|
1254
|
+
locale: options.locale,
|
|
1255
|
+
detect: options.detect,
|
|
1256
|
+
onCountryChange: options.onCountryChange
|
|
1257
|
+
});
|
|
1258
|
+
const {
|
|
1259
|
+
digits,
|
|
1260
|
+
formatter,
|
|
1261
|
+
displayPlaceholder,
|
|
1262
|
+
displayValue,
|
|
1263
|
+
full,
|
|
1264
|
+
fullFormatted,
|
|
1265
|
+
isComplete,
|
|
1266
|
+
isEmpty,
|
|
1267
|
+
shouldShowWarn
|
|
1268
|
+
} = useFormatter({
|
|
1269
|
+
country,
|
|
1270
|
+
value: options.value,
|
|
1271
|
+
onChange: options.onChange,
|
|
1272
|
+
onPhoneChange: options.onPhoneChange
|
|
1273
|
+
});
|
|
1274
|
+
const { handleBeforeInput, handleInput, handleKeydown, handlePaste } = useInputHandlers({
|
|
1275
|
+
formatter,
|
|
1276
|
+
digits,
|
|
1277
|
+
onChange: options.onChange
|
|
1278
|
+
});
|
|
1279
|
+
vue.onMounted(() => {
|
|
1280
|
+
const el = inputRef.value;
|
|
1281
|
+
if (!el) return;
|
|
1282
|
+
el.setAttribute("type", "tel");
|
|
1283
|
+
el.setAttribute("inputmode", "tel");
|
|
1284
|
+
});
|
|
1285
|
+
vue.watchEffect(
|
|
1286
|
+
() => {
|
|
1287
|
+
const el = inputRef.value;
|
|
1288
|
+
if (!el) return;
|
|
1289
|
+
el.value = displayValue.value;
|
|
1290
|
+
el.setAttribute("placeholder", displayPlaceholder.value);
|
|
1291
|
+
},
|
|
1292
|
+
{ flush: "post" }
|
|
1293
|
+
);
|
|
1294
|
+
vue.onMounted(() => {
|
|
1295
|
+
const el = inputRef.value;
|
|
1296
|
+
if (!el) return;
|
|
1297
|
+
el.addEventListener("beforeinput", handleBeforeInput);
|
|
1298
|
+
el.addEventListener("input", handleInput);
|
|
1299
|
+
el.addEventListener("keydown", handleKeydown);
|
|
1300
|
+
el.addEventListener("paste", handlePaste);
|
|
1301
|
+
});
|
|
1302
|
+
vue.onUnmounted(() => {
|
|
1303
|
+
const el = inputRef.value;
|
|
1304
|
+
if (!el) return;
|
|
1305
|
+
el.removeEventListener("beforeinput", handleBeforeInput);
|
|
1306
|
+
el.removeEventListener("input", handleInput);
|
|
1307
|
+
el.removeEventListener("keydown", handleKeydown);
|
|
1308
|
+
el.removeEventListener("paste", handlePaste);
|
|
1309
|
+
});
|
|
1310
|
+
const clear = () => {
|
|
1311
|
+
options.onChange("");
|
|
1312
|
+
};
|
|
1313
|
+
return {
|
|
1314
|
+
inputRef,
|
|
1315
|
+
digits,
|
|
1316
|
+
full,
|
|
1317
|
+
fullFormatted,
|
|
1318
|
+
isComplete,
|
|
1319
|
+
isEmpty,
|
|
1320
|
+
shouldShowWarn,
|
|
1321
|
+
country,
|
|
1322
|
+
setCountry,
|
|
1323
|
+
clear
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1388
1326
|
function install(app) {
|
|
1389
1327
|
app.component("PhoneInput", PhoneInput);
|
|
1390
1328
|
app.directive("phone-mask", vPhoneMask);
|
|
@@ -1393,7 +1331,7 @@ var lib = (function(exports, vue) {
|
|
|
1393
1331
|
install
|
|
1394
1332
|
};
|
|
1395
1333
|
const PMaskHelpers = {
|
|
1396
|
-
getFlagEmoji:
|
|
1334
|
+
getFlagEmoji: k,
|
|
1397
1335
|
countPlaceholders,
|
|
1398
1336
|
formatDigitsWithMap,
|
|
1399
1337
|
pickMaskVariant,
|
|
@@ -1404,6 +1342,7 @@ var lib = (function(exports, vue) {
|
|
|
1404
1342
|
exports.PhoneInput = PhoneInput;
|
|
1405
1343
|
exports.default = index;
|
|
1406
1344
|
exports.install = install;
|
|
1345
|
+
exports.usePhoneMask = usePhoneMask;
|
|
1407
1346
|
exports.vPhoneMask = vPhoneMask;
|
|
1408
1347
|
exports.vPhoneMaskSetCountry = updateCountry;
|
|
1409
1348
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|