@alikhalilll/a-tel-input 1.0.2 → 1.1.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.
Files changed (39) hide show
  1. package/README.md +585 -72
  2. package/dist/_chunks/types.d.ts +661 -0
  3. package/dist/_chunks/types.js +52 -0
  4. package/dist/_chunks/types.js.map +1 -0
  5. package/dist/_chunks/usePhoneValidation.js +539 -0
  6. package/dist/_chunks/usePhoneValidation.js.map +1 -0
  7. package/dist/index.cjs +444 -683
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +122 -587
  10. package/dist/index.d.ts +122 -587
  11. package/dist/index.js +427 -646
  12. package/dist/index.js.map +1 -1
  13. package/dist/styles.css +3 -2
  14. package/dist/vee-validate/index.cjs +113 -0
  15. package/dist/vee-validate/index.cjs.map +1 -0
  16. package/dist/vee-validate/index.d.cts +86 -0
  17. package/dist/vee-validate/index.d.ts +86 -0
  18. package/dist/vee-validate/index.js +112 -0
  19. package/dist/vee-validate/index.js.map +1 -0
  20. package/dist/zod/index.cjs +211 -0
  21. package/dist/zod/index.cjs.map +1 -0
  22. package/dist/zod/index.d.cts +65 -0
  23. package/dist/zod/index.d.ts +65 -0
  24. package/dist/zod/index.js +208 -0
  25. package/dist/zod/index.js.map +1 -0
  26. package/package.json +33 -3
  27. package/src/components/ATelInput.vue +206 -66
  28. package/src/composables/useCountryDetection.ts +28 -11
  29. package/src/composables/useCountryMatching.ts +160 -20
  30. package/src/composables/useCountrySelection.ts +71 -0
  31. package/src/composables/usePhoneValidation.ts +81 -18
  32. package/src/composables/useSyncedModel.ts +80 -0
  33. package/src/composables/useTelInputValidation.ts +50 -11
  34. package/src/index.ts +2 -0
  35. package/src/types.ts +80 -0
  36. package/src/vee-validate/index.ts +2 -0
  37. package/src/vee-validate/useTelField.ts +202 -0
  38. package/src/zod/index.ts +259 -0
  39. package/web-types.json +44 -1
package/dist/index.cjs CHANGED
@@ -1,33 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp$1 = Object.defineProperty;
5
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames$1 = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
9
- var __copyProps$1 = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp$1.call(to, key) && key !== except) __defProp$1(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps$1(isNodeMode || !mod || !mod.__esModule ? __defProp$1(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
2
+ const require_usePhoneValidation = require("./_chunks/usePhoneValidation.js");
3
+ const require_types = require("./_chunks/types.js");
24
4
  let vue = require("vue");
25
- vue = __toESM(vue, 1);
5
+ vue = require_usePhoneValidation.__toESM(vue, 1);
26
6
  let libphonenumber_js = require("libphonenumber-js");
27
- let libphonenumber_js_examples_mobile_json = require("libphonenumber-js/examples.mobile.json");
28
- libphonenumber_js_examples_mobile_json = __toESM(libphonenumber_js_examples_mobile_json, 1);
29
7
  let _vueuse_core = require("@vueuse/core");
30
- let class_variance_authority = require("class-variance-authority");
31
8
  let aria_hidden = require("aria-hidden");
32
9
  //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
33
10
  function r$2(e) {
@@ -3428,508 +3405,6 @@ function cn$2(...inputs) {
3428
3405
  return twMerge$2(clsx$2(inputs));
3429
3406
  }
3430
3407
  //#endregion
3431
- //#region src/utils/digits.ts
3432
- /**
3433
- * Alternative-numeral support. Phone numbers are routinely entered with the digits of the
3434
- * user's own script — Arabic-Indic, Persian/Urdu, Devanagari, Bengali. `libphonenumber-js`
3435
- * and our own `\d` cleanup only understand ASCII `0-9`, so anything else silently becomes
3436
- * an empty number. `normalizeDigits` folds those scripts down to ASCII before validation.
3437
- */
3438
- /**
3439
- * Base code points of contiguous decimal-digit blocks. Each block runs `base`‥`base+9`
3440
- * for digit `0`‥`9`, so the ASCII digit is `codePoint - base`. Add a script by appending
3441
- * one entry here.
3442
- */
3443
- const LOCALE_DIGIT_RANGES = [
3444
- {
3445
- name: "arabic-indic",
3446
- base: 1632
3447
- },
3448
- {
3449
- name: "extended-arabic",
3450
- base: 1776
3451
- },
3452
- {
3453
- name: "devanagari",
3454
- base: 2406
3455
- },
3456
- {
3457
- name: "bengali",
3458
- base: 2534
3459
- }
3460
- ];
3461
- /** Lookup of every non-ASCII digit code point → its ASCII character. */
3462
- const DIGIT_MAP = (() => {
3463
- const map = /* @__PURE__ */ new Map();
3464
- for (const { base } of LOCALE_DIGIT_RANGES) for (let d = 0; d <= 9; d++) map.set(base + d, String(d));
3465
- return map;
3466
- })();
3467
- /**
3468
- * Replace any supported non-ASCII decimal digit with its ASCII equivalent. Every other
3469
- * character (spaces, `+`, separators, letters) is left untouched — callers still run their
3470
- * own `\D` cleanup afterwards.
3471
- */
3472
- function normalizeDigits(input) {
3473
- const str = String(input ?? "");
3474
- let out = "";
3475
- for (const ch of str) {
3476
- const cp = ch.codePointAt(0);
3477
- out += cp != null && DIGIT_MAP.get(cp) || ch;
3478
- }
3479
- return out;
3480
- }
3481
- //#endregion
3482
- //#region src/composables/usePhoneValidation.ts
3483
- /**
3484
- * Country list + phone validation, framework-agnostic.
3485
- *
3486
- * Ported from the reference @pkgs/ui ATelInput composable with these cleanups:
3487
- * - Drop Nuxt-only `process.client` checks → use plain `typeof window !== 'undefined'`.
3488
- * - Drop Arabic default placeholder; let consumers pass their own.
3489
- * - Expand the offline fallback list from 2 → ~20 of the most-populated countries.
3490
- * - Keep REST Countries fetch + localStorage cache + libphonenumber-js examples + fast `search_key`.
3491
- */
3492
- const STORAGE_KEY = "ali_ui_phone_countries_v1";
3493
- const REST_COUNTRIES_URL = "https://restcountries.com/v3.1/all?fields=name,cca2,idd,flags";
3494
- const EX = libphonenumber_js_examples_mobile_json.default;
3495
- const isBrowser$1 = () => typeof window !== "undefined";
3496
- function toDigits(v) {
3497
- return normalizeDigits(String(v ?? "")).replace(/\D/g, "");
3498
- }
3499
- /**
3500
- * Render an ASCII digit string in a locale's numeral system (e.g. `'ar'` → `٠-٩`).
3501
- * Used only for display hints — falls back to ASCII if the locale is unknown.
3502
- */
3503
- function localizeDigits(digits, locale) {
3504
- if (!locale) return digits;
3505
- try {
3506
- const fmt = new Intl.NumberFormat(locale, { useGrouping: false });
3507
- return digits.replace(/[0-9]/g, (d) => fmt.format(Number(d)));
3508
- } catch {
3509
- return digits;
3510
- }
3511
- }
3512
- function ensurePlusDial(dial) {
3513
- const d = toDigits(dial);
3514
- return d ? `+${d}` : "";
3515
- }
3516
- function normalizeIso2(iso2) {
3517
- return String(iso2 ?? "").trim().toUpperCase();
3518
- }
3519
- function dropLeadingZeros(digits) {
3520
- return String(digits ?? "").replace(/^0+/, "");
3521
- }
3522
- function buildFullE164(dial, digits) {
3523
- const dialClean = ensurePlusDial(dial);
3524
- const nsn = dropLeadingZeros(toDigits(digits));
3525
- return dialClean && nsn ? `${dialClean}${nsn}` : null;
3526
- }
3527
- function inferLengthFromExample(national) {
3528
- const d = toDigits(national);
3529
- if (!d) return {
3530
- min: null,
3531
- max: null
3532
- };
3533
- const n = d.length;
3534
- return {
3535
- min: Math.max(4, n - 2),
3536
- max: n + 2
3537
- };
3538
- }
3539
- function buildDialCode(idd) {
3540
- const root = idd?.root?.trim();
3541
- if (!root || !root.startsWith("+")) return null;
3542
- const out = `${root}${idd?.suffixes?.[0]?.trim() ?? ""}`;
3543
- return out.startsWith("+") ? out : null;
3544
- }
3545
- function normalizeSearchKey(input) {
3546
- return String(input ?? "").toLowerCase().replace(/\s+/g, " ").trim().replace(/[^\p{L}\p{N}+ ]/gu, "");
3547
- }
3548
- /**
3549
- * Return a copy of the country list with display names localized to `locale` via
3550
- * `Intl.DisplayNames`. `search_key` is rebuilt (keeping the English name too) so search
3551
- * still matches either spelling. Unknown locales / regions fall back to the English name.
3552
- */
3553
- function localizeCountries(list, locale) {
3554
- if (!locale) return list;
3555
- let display;
3556
- try {
3557
- display = new Intl.DisplayNames([locale], { type: "region" });
3558
- } catch {
3559
- return list;
3560
- }
3561
- return list.map((c) => {
3562
- let localized = c.raw_data.name;
3563
- try {
3564
- localized = display.of(c.raw_data.iso2) || c.raw_data.name;
3565
- } catch {}
3566
- if (localized === c.raw_data.name) return c;
3567
- const dial = c.raw_data.dial_code;
3568
- return {
3569
- ...c,
3570
- label: `${localized} (${dial})`,
3571
- search_key: normalizeSearchKey(`${localized} ${c.raw_data.name} ${dial} ${c.raw_data.iso2} ${c.raw_data.dial_digits}`),
3572
- raw_data: {
3573
- ...c.raw_data,
3574
- name: localized
3575
- }
3576
- };
3577
- });
3578
- }
3579
- function makeFallback(iso2, name, dial) {
3580
- const dialDigits = toDigits(dial);
3581
- return {
3582
- label: `${name} (+${dialDigits})`,
3583
- value: iso2,
3584
- search_key: normalizeSearchKey(`${name} +${dialDigits} ${iso2}`),
3585
- raw_data: {
3586
- iso2,
3587
- dial_code: `+${dialDigits}`,
3588
- dial_digits: dialDigits,
3589
- name,
3590
- flag: `https://flagcdn.com/w40/${iso2.toLowerCase()}.png`,
3591
- source: "fallback",
3592
- original: {}
3593
- }
3594
- };
3595
- }
3596
- const FALLBACK_COUNTRIES = [
3597
- makeFallback("SA", "Saudi Arabia", "+966"),
3598
- makeFallback("EG", "Egypt", "+20"),
3599
- makeFallback("AE", "United Arab Emirates", "+971"),
3600
- makeFallback("US", "United States", "+1"),
3601
- makeFallback("GB", "United Kingdom", "+44"),
3602
- makeFallback("DE", "Germany", "+49"),
3603
- makeFallback("FR", "France", "+33"),
3604
- makeFallback("ES", "Spain", "+34"),
3605
- makeFallback("IT", "Italy", "+39"),
3606
- makeFallback("TR", "Turkey", "+90"),
3607
- makeFallback("RU", "Russia", "+7"),
3608
- makeFallback("CN", "China", "+86"),
3609
- makeFallback("IN", "India", "+91"),
3610
- makeFallback("JP", "Japan", "+81"),
3611
- makeFallback("KR", "South Korea", "+82"),
3612
- makeFallback("BR", "Brazil", "+55"),
3613
- makeFallback("MX", "Mexico", "+52"),
3614
- makeFallback("CA", "Canada", "+1"),
3615
- makeFallback("AU", "Australia", "+61"),
3616
- makeFallback("NG", "Nigeria", "+234"),
3617
- makeFallback("PK", "Pakistan", "+92"),
3618
- makeFallback("ID", "Indonesia", "+62")
3619
- ];
3620
- function usePhoneValidation() {
3621
- const countries = (0, vue.ref)([]);
3622
- const isCountriesLoading = (0, vue.ref)(false);
3623
- const byValue = (0, vue.ref)(/* @__PURE__ */ new Map());
3624
- const byDialDigits = (0, vue.ref)(/* @__PURE__ */ new Map());
3625
- function rebuildIndexes(list) {
3626
- const valueMap = /* @__PURE__ */ new Map();
3627
- const dialMap = /* @__PURE__ */ new Map();
3628
- for (const item of list) {
3629
- valueMap.set(item.value, item);
3630
- const dial = item.raw_data.dial_digits;
3631
- if (dial) {
3632
- const bucket = dialMap.get(dial) ?? [];
3633
- bucket.push(item);
3634
- dialMap.set(dial, bucket);
3635
- }
3636
- }
3637
- byValue.value = valueMap;
3638
- byDialDigits.value = dialMap;
3639
- }
3640
- function upsertCountries(list) {
3641
- countries.value = list;
3642
- rebuildIndexes(list);
3643
- }
3644
- function normalizeRestCountries(list) {
3645
- const out = [];
3646
- for (const c of list) {
3647
- const name = c?.name?.common?.trim();
3648
- const iso2 = normalizeIso2(c?.cca2);
3649
- const dial = buildDialCode(c?.idd);
3650
- const flag = c?.flags?.png?.trim() || c?.flags?.svg?.trim() || null;
3651
- if (!name || !iso2 || !dial) continue;
3652
- const dialDigits = toDigits(dial);
3653
- const search_key = normalizeSearchKey(`${name} ${dial} ${iso2} ${dialDigits}`);
3654
- out.push({
3655
- label: `${name} (${dial})`,
3656
- value: iso2,
3657
- search_key,
3658
- raw_data: {
3659
- iso2,
3660
- dial_code: dial,
3661
- dial_digits: dialDigits,
3662
- name,
3663
- flag,
3664
- source: "restcountries",
3665
- original: c
3666
- }
3667
- });
3668
- }
3669
- const map = /* @__PURE__ */ new Map();
3670
- for (const item of out) {
3671
- const prev = map.get(item.value);
3672
- if (!prev) {
3673
- map.set(item.value, item);
3674
- continue;
3675
- }
3676
- const prevScore = (prev.raw_data.flag ? 1 : 0) + (prev.raw_data.dial_code ? 1 : 0);
3677
- if ((item.raw_data.flag ? 1 : 0) + (item.raw_data.dial_code ? 1 : 0) > prevScore) map.set(item.value, item);
3678
- }
3679
- return Array.from(map.values()).sort((a, b) => a.raw_data.name.localeCompare(b.raw_data.name));
3680
- }
3681
- async function getCountries(options) {
3682
- const force = Boolean(options?.force);
3683
- if (!force && countries.value.length) return countries.value;
3684
- if (!force && isBrowser$1()) try {
3685
- const cached = localStorage.getItem(STORAGE_KEY);
3686
- if (cached) {
3687
- const parsed = JSON.parse(cached);
3688
- if (Array.isArray(parsed) && parsed.length) {
3689
- upsertCountries(parsed);
3690
- return countries.value;
3691
- }
3692
- }
3693
- } catch {}
3694
- isCountriesLoading.value = true;
3695
- try {
3696
- const res = await fetch(REST_COUNTRIES_URL);
3697
- if (!res.ok) throw new Error(`Failed to fetch countries: ${res.status}`);
3698
- const normalized = normalizeRestCountries(await res.json());
3699
- upsertCountries(normalized.length ? normalized : FALLBACK_COUNTRIES);
3700
- if (isBrowser$1()) try {
3701
- localStorage.setItem(STORAGE_KEY, JSON.stringify(countries.value));
3702
- } catch {}
3703
- return countries.value;
3704
- } catch {
3705
- upsertCountries(FALLBACK_COUNTRIES);
3706
- return countries.value;
3707
- } finally {
3708
- isCountriesLoading.value = false;
3709
- }
3710
- }
3711
- function searchCountries(keyword, limit = 50) {
3712
- const q = normalizeSearchKey(keyword);
3713
- if (!q) return countries.value.slice(0, limit);
3714
- const res = [];
3715
- for (const item of countries.value) if (item.search_key.includes(q)) {
3716
- res.push(item);
3717
- if (res.length >= limit) break;
3718
- }
3719
- return res;
3720
- }
3721
- function getCountryByValue(value) {
3722
- return byValue.value.get(normalizeIso2(value)) ?? null;
3723
- }
3724
- function getCountriesByDial(dial) {
3725
- return byDialDigits.value.get(toDigits(dial)) ?? [];
3726
- }
3727
- function getRequiredInfo(country, locale) {
3728
- const iso2 = normalizeIso2(country.iso2);
3729
- if (!iso2) return null;
3730
- try {
3731
- const example = (0, libphonenumber_js.getExampleNumber)(iso2, EX);
3732
- const exampleNational = example?.formatNational?.() ?? "";
3733
- const exampleE164 = example?.format?.("E.164") ?? "";
3734
- const inferred = inferLengthFromExample(exampleNational);
3735
- const dial_code = country.dial_code ? ensurePlusDial(country.dial_code) : exampleE164 ? `+${example?.countryCallingCode}` : "";
3736
- const digitsExample = toDigits(exampleNational);
3737
- return {
3738
- iso2,
3739
- dial_code,
3740
- placeholder: "",
3741
- example_national: exampleNational,
3742
- example_e164: exampleE164,
3743
- national_number_length: inferred,
3744
- format_hint: digitsExample ? `e.g. ${localizeDigits(digitsExample, locale)}` : ""
3745
- };
3746
- } catch {
3747
- return null;
3748
- }
3749
- }
3750
- function validate(input) {
3751
- const country = input.country ?? null;
3752
- if (!country?.iso2) return {
3753
- ok: false,
3754
- reason: "missing_country",
3755
- country: null,
3756
- phone: {
3757
- raw: ("phone" in input ? input.phone : null) ?? null,
3758
- digits: ""
3759
- },
3760
- full_phone: null,
3761
- required: null
3762
- };
3763
- const iso2 = normalizeIso2(country.iso2);
3764
- const required = getRequiredInfo({
3765
- iso2,
3766
- dial_code: country.dial_code
3767
- }, input.locale);
3768
- if (!required) return {
3769
- ok: false,
3770
- reason: "country_not_supported",
3771
- country: {
3772
- iso2,
3773
- dial_code: ensurePlusDial(country.dial_code)
3774
- },
3775
- phone: {
3776
- raw: ("phone" in input ? input.phone : null) ?? null,
3777
- digits: ""
3778
- },
3779
- full_phone: null,
3780
- required: null
3781
- };
3782
- if (!("phone" in input)) return {
3783
- ok: true,
3784
- reason: null,
3785
- country: {
3786
- iso2: required.iso2,
3787
- dial_code: required.dial_code
3788
- },
3789
- phone: {
3790
- raw: null,
3791
- digits: ""
3792
- },
3793
- full_phone: null,
3794
- required
3795
- };
3796
- const raw = input.phone;
3797
- const digits = toDigits(raw);
3798
- if (!raw || !String(raw).trim()) return {
3799
- ok: true,
3800
- reason: null,
3801
- country: {
3802
- iso2: required.iso2,
3803
- dial_code: required.dial_code
3804
- },
3805
- phone: {
3806
- raw: raw ?? null,
3807
- digits: ""
3808
- },
3809
- full_phone: null,
3810
- required
3811
- };
3812
- if (String(raw).replace(/\s+/g, "").match(/[^\d+]/)) return {
3813
- ok: false,
3814
- reason: "phone_has_non_digits",
3815
- country: {
3816
- iso2: required.iso2,
3817
- dial_code: required.dial_code
3818
- },
3819
- phone: {
3820
- raw,
3821
- digits
3822
- },
3823
- full_phone: buildFullE164(required.dial_code, digits),
3824
- required
3825
- };
3826
- const nsn = dropLeadingZeros(digits);
3827
- const { min, max } = required.national_number_length;
3828
- if (min !== null && nsn.length < min) return {
3829
- ok: false,
3830
- reason: "too_short",
3831
- country: {
3832
- iso2: required.iso2,
3833
- dial_code: required.dial_code
3834
- },
3835
- phone: {
3836
- raw,
3837
- digits
3838
- },
3839
- full_phone: buildFullE164(required.dial_code, digits),
3840
- required,
3841
- details: {
3842
- min,
3843
- actual: nsn.length
3844
- }
3845
- };
3846
- if (max !== null && nsn.length > max) return {
3847
- ok: false,
3848
- reason: "too_long",
3849
- country: {
3850
- iso2: required.iso2,
3851
- dial_code: required.dial_code
3852
- },
3853
- phone: {
3854
- raw,
3855
- digits
3856
- },
3857
- full_phone: buildFullE164(required.dial_code, digits),
3858
- required,
3859
- details: {
3860
- max,
3861
- actual: nsn.length
3862
- }
3863
- };
3864
- const full = buildFullE164(required.dial_code, digits) ?? String(raw);
3865
- try {
3866
- if (!(0, libphonenumber_js.isValidPhoneNumber)(full, iso2)) {
3867
- const parsed = (0, libphonenumber_js.parsePhoneNumberFromString)(full, iso2);
3868
- return {
3869
- ok: false,
3870
- reason: "invalid_phone",
3871
- country: {
3872
- iso2: required.iso2,
3873
- dial_code: required.dial_code
3874
- },
3875
- phone: {
3876
- raw,
3877
- digits
3878
- },
3879
- full_phone: parsed?.number ?? null,
3880
- required,
3881
- details: {
3882
- type: parsed?.getType?.() ?? null,
3883
- possible: parsed?.isPossible?.() ?? null,
3884
- country: parsed?.country ?? null
3885
- }
3886
- };
3887
- }
3888
- const parsed = (0, libphonenumber_js.parsePhoneNumberFromString)(full, iso2);
3889
- return {
3890
- ok: true,
3891
- reason: null,
3892
- country: {
3893
- iso2: required.iso2,
3894
- dial_code: required.dial_code
3895
- },
3896
- phone: {
3897
- raw,
3898
- digits
3899
- },
3900
- full_phone: parsed?.number ?? full,
3901
- required
3902
- };
3903
- } catch (e) {
3904
- return {
3905
- ok: false,
3906
- reason: "parse_failed",
3907
- country: {
3908
- iso2: required.iso2,
3909
- dial_code: required.dial_code
3910
- },
3911
- phone: {
3912
- raw,
3913
- digits
3914
- },
3915
- full_phone: buildFullE164(required.dial_code, digits),
3916
- required,
3917
- details: { error: e?.message ?? String(e) }
3918
- };
3919
- }
3920
- }
3921
- return {
3922
- countries,
3923
- isCountriesLoading,
3924
- getCountries,
3925
- searchCountries,
3926
- getCountryByValue,
3927
- getCountriesByDial,
3928
- getRequiredInfo,
3929
- validate
3930
- };
3931
- }
3932
- //#endregion
3933
3408
  //#region src/composables/useCountryDetection.ts
3934
3409
  /**
3935
3410
  * Best-effort country detection chain: IP geolocation → timezone → navigator.language → fallback.
@@ -4039,24 +3514,32 @@ function tryLocale() {
4039
3514
  return null;
4040
3515
  }
4041
3516
  }
3517
+ const inflightIpFetch = /* @__PURE__ */ new Map();
4042
3518
  async function tryIp(endpoint, timeoutMs) {
4043
3519
  if (!isBrowser() || typeof fetch !== "function") return null;
3520
+ const existing = inflightIpFetch.get(endpoint);
3521
+ if (existing) return existing;
4044
3522
  const controller = new AbortController();
4045
3523
  const timer = setTimeout(() => controller.abort(), timeoutMs);
4046
- try {
4047
- const res = await fetch(endpoint, {
4048
- signal: controller.signal,
4049
- credentials: "omit"
4050
- });
4051
- if (!res.ok) return null;
4052
- const data = await res.json();
4053
- const code = (data.country_code ?? data.country ?? "").toString().toUpperCase();
4054
- return /^[A-Z]{2}$/.test(code) ? code : null;
4055
- } catch {
4056
- return null;
4057
- } finally {
4058
- clearTimeout(timer);
4059
- }
3524
+ const promise = (async () => {
3525
+ try {
3526
+ const res = await fetch(endpoint, {
3527
+ signal: controller.signal,
3528
+ credentials: "omit"
3529
+ });
3530
+ if (!res.ok) return null;
3531
+ const data = await res.json();
3532
+ const code = (data.country_code ?? data.country ?? "").toString().toUpperCase();
3533
+ return /^[A-Z]{2}$/.test(code) ? code : null;
3534
+ } catch {
3535
+ return null;
3536
+ } finally {
3537
+ clearTimeout(timer);
3538
+ inflightIpFetch.delete(endpoint);
3539
+ }
3540
+ })();
3541
+ inflightIpFetch.set(endpoint, promise);
3542
+ return promise;
4060
3543
  }
4061
3544
  function readCache() {
4062
3545
  if (!isBrowser()) return null;
@@ -4122,6 +3605,11 @@ function useCountryDetection(opts = {}) {
4122
3605
  }
4123
3606
  //#endregion
4124
3607
  //#region src/composables/useCountryMatching.ts
3608
+ /** Cached snapshot of every country libphonenumber knows about (~250 ISO2 codes).
3609
+ * Used by tier 2 of `matchLeadingDialCode` as the last-resort iteration so detection
3610
+ * works for *every* country, not just the popular ones in the bundled fallback list.
3611
+ * Cached at module load — `getCountries()` is a static metadata table, no I/O. */
3612
+ const ALL_LIBPHONENUMBER_ISO2 = (0, libphonenumber_js.getCountries)();
4125
3613
  /** Synchronous dial-digit → ISO2 fallback for common countries, used when the async
4126
3614
  * REST Countries fetch hasn't populated `getCountriesByDial`'s index yet at setup. */
4127
3615
  const DIAL_TO_ISO2_FALLBACK = {
@@ -4188,6 +3676,58 @@ const DIAL_TO_ISO2_FALLBACK = {
4188
3676
  /** localStorage key for the user's most recently picked countries. Used as a
4189
3677
  * tie-breaker when multiple countries share a dial code (e.g. all NANP). */
4190
3678
  const COUNTRY_RECENTS_KEY = "ali_ui_country_recents_v1";
3679
+ /** ISO2 codes iterated by tier 2 of `matchLeadingDialCode` when looking for a country
3680
+ * that accepts a local-format input as valid. Mirrors the `FALLBACK_COUNTRIES` list in
3681
+ * {@link usePhoneValidation} (kept in sync by tests + by being short and obvious).
3682
+ * Order matters — earlier entries get priority when multiple countries would each
3683
+ * validate the same input. Built around the most-populated / most-likely countries. */
3684
+ const FALLBACK_ISO2_LIST = [
3685
+ "SA",
3686
+ "EG",
3687
+ "AE",
3688
+ "US",
3689
+ "GB",
3690
+ "DE",
3691
+ "FR",
3692
+ "ES",
3693
+ "IT",
3694
+ "TR",
3695
+ "RU",
3696
+ "CN",
3697
+ "IN",
3698
+ "JP",
3699
+ "KR",
3700
+ "BR",
3701
+ "MX",
3702
+ "CA",
3703
+ "AU",
3704
+ "NG",
3705
+ "PK",
3706
+ "ID"
3707
+ ];
3708
+ /** Build a minimal `CountryOption` from libphonenumber metadata when the async REST
3709
+ * Countries list hasn't loaded the entry yet. Used so country **detection** works
3710
+ * generically for any libphonenumber country, not just the ~22 in the offline
3711
+ * fallback list. The picker will overwrite this synthetic record with the real one
3712
+ * (with localized name + flag) as soon as `getCountries()` resolves. */
3713
+ function buildSyntheticCountry(iso2, dialDigits) {
3714
+ const ISO2 = iso2.toUpperCase();
3715
+ const digits = String(dialDigits).replace(/\D/g, "");
3716
+ return {
3717
+ label: `${ISO2} (+${digits})`,
3718
+ value: ISO2,
3719
+ search_key: `${ISO2.toLowerCase()} +${digits} ${digits}`,
3720
+ raw_data: {
3721
+ iso2: ISO2,
3722
+ dial_code: `+${digits}`,
3723
+ dial_digits: digits,
3724
+ name: ISO2,
3725
+ flag: `https://flagcdn.com/w40/${ISO2.toLowerCase()}.png`,
3726
+ source: "fallback",
3727
+ original: {}
3728
+ }
3729
+ };
3730
+ }
4191
3731
  function readRecents() {
4192
3732
  if (typeof window === "undefined") return [];
4193
3733
  try {
@@ -4233,38 +3773,75 @@ function useCountryMatching(deps) {
4233
3773
  const n = Number(digits);
4234
3774
  return Number.isFinite(n) ? n : null;
4235
3775
  }
3776
+ const MATCHER_CACHE_MAX = 128;
3777
+ const matcherCache = /* @__PURE__ */ new Map();
3778
+ function readMatcherCache(key) {
3779
+ if (!matcherCache.has(key)) return void 0;
3780
+ const value = matcherCache.get(key);
3781
+ matcherCache.delete(key);
3782
+ matcherCache.set(key, value);
3783
+ return value;
3784
+ }
3785
+ function writeMatcherCache(key, value) {
3786
+ if (matcherCache.size >= MATCHER_CACHE_MAX) {
3787
+ const oldest = matcherCache.keys().next().value;
3788
+ if (oldest !== void 0) matcherCache.delete(oldest);
3789
+ }
3790
+ matcherCache.set(key, value);
3791
+ }
4236
3792
  /** Three-tier match of the leading digits to a country:
4237
3793
  * 1. libphonenumber international parse (handles NANP disambiguation).
4238
- * 2. libphonenumber national-format parse using `hintCountry` (handles local
4239
- * formats like Egyptian `01066105963` with no dial-code prefix).
3794
+ * 2. libphonenumber national-format parse, iterating through candidate hint
3795
+ * countries (handles local formats like Egyptian `01066105963` with no
3796
+ * dial-code prefix). Universal coverage via `getCountries()`.
4240
3797
  * 3. Longest-prefix match against the dial-digits index, with the current
4241
- * selection / recents as tie-breakers when multiple countries share a code. */
3798
+ * selection / recents as tie-breakers when multiple countries share a code.
3799
+ *
3800
+ * Results are LRU-cached per input + context to avoid re-paying tier-2 iteration
3801
+ * cost when the user backspaces and retypes the same prefix. */
4242
3802
  function matchLeadingDialCode(digits, options = {}) {
4243
3803
  if (!digits) return null;
4244
3804
  const { hintCountry, currentIso2 } = options;
3805
+ const cacheKey = `${digits}|${hintCountry ?? ""}|${currentIso2 ?? ""}`;
3806
+ const cached = readMatcherCache(cacheKey);
3807
+ if (cached !== void 0) return cached;
3808
+ const result = runMatch(digits, hintCountry, currentIso2);
3809
+ writeMatcherCache(cacheKey, result);
3810
+ return result;
3811
+ }
3812
+ function runMatch(digits, hintCountry, currentIso2) {
4245
3813
  try {
4246
3814
  const parsed = (0, libphonenumber_js.parsePhoneNumberFromString)(`+${digits}`);
4247
- if (parsed?.country && parsed.countryCallingCode) {
4248
- const parsedCountry = getCountryByValue(parsed.country);
4249
- if (parsedCountry) return {
4250
- country: parsedCountry,
4251
- nationalNumber: String(parsed.nationalNumber ?? "")
4252
- };
4253
- }
4254
- } catch {}
4255
- if (hintCountry && digits.length >= 4) try {
4256
- const parsed = (0, libphonenumber_js.parsePhoneNumberFromString)(digits, hintCountry);
4257
- if (parsed?.isValid()) {
4258
- const matched = getCountryByValue(parsed.country || hintCountry);
4259
- if (matched) return {
4260
- country: matched,
4261
- nationalNumber: String(parsed.nationalNumber ?? "")
4262
- };
4263
- }
3815
+ if (parsed?.country && parsed.countryCallingCode) return {
3816
+ country: getCountryByValue(parsed.country) ?? buildSyntheticCountry(parsed.country, String(parsed.countryCallingCode)),
3817
+ nationalNumber: String(parsed.nationalNumber ?? "")
3818
+ };
4264
3819
  } catch {}
3820
+ if (digits.length >= 4) {
3821
+ const candidates = /* @__PURE__ */ new Set();
3822
+ if (hintCountry) candidates.add(hintCountry.toUpperCase());
3823
+ if (currentIso2) candidates.add(currentIso2.toUpperCase());
3824
+ for (const recent of readRecents()) candidates.add(recent.toUpperCase());
3825
+ for (const fallback of FALLBACK_ISO2_LIST) candidates.add(fallback);
3826
+ for (const all of ALL_LIBPHONENUMBER_ISO2) candidates.add(all);
3827
+ for (const iso2 of candidates) try {
3828
+ const parsed = (0, libphonenumber_js.parsePhoneNumberFromString)(digits, iso2);
3829
+ if (parsed?.isValid()) {
3830
+ const resolvedIso2 = parsed.country || iso2;
3831
+ return {
3832
+ country: getCountryByValue(resolvedIso2) ?? buildSyntheticCountry(resolvedIso2, String(parsed.countryCallingCode ?? "")),
3833
+ nationalNumber: String(parsed.nationalNumber ?? "")
3834
+ };
3835
+ }
3836
+ } catch {}
3837
+ }
4265
3838
  for (let len = Math.min(3, digits.length); len >= 1; len--) {
4266
3839
  const prefix = digits.slice(0, len);
4267
- const group = getCountriesByDial(prefix);
3840
+ let group = getCountriesByDial(prefix);
3841
+ if (!group.length) {
3842
+ const iso2 = DIAL_TO_ISO2_FALLBACK[prefix];
3843
+ if (iso2) group = [getCountryByValue(iso2) ?? buildSyntheticCountry(iso2, prefix)];
3844
+ }
4268
3845
  if (!group.length) continue;
4269
3846
  const nationalNumber = digits.slice(prefix.length);
4270
3847
  if (group.length === 1) return {
@@ -4336,18 +3913,36 @@ function useTelInputValidation(deps, inputs, config) {
4336
3913
  phone: inputs.phone.value ?? "",
4337
3914
  locale: config.locale()
4338
3915
  }));
3916
+ const externalErrorActive = (0, vue.computed)(() => {
3917
+ const e = config.externalError();
3918
+ return typeof e === "string" && e.length > 0;
3919
+ });
4339
3920
  const validationState = (0, vue.computed)(() => {
3921
+ if (externalErrorActive.value) return "error";
4340
3922
  if (!inputs.phone.value) return "idle";
4341
3923
  return validation.value.ok ? "valid" : "error";
4342
3924
  });
4343
- const visibleValidationState = (0, vue.computed)(() => inputs.hasFinishedTyping.value ? validationState.value : "idle");
3925
+ const visibleValidationState = (0, vue.computed)(() => {
3926
+ if (externalErrorActive.value) return "error";
3927
+ const mode = config.validateOn() ?? "change";
3928
+ if (mode === "eager") return validationState.value;
3929
+ if (mode === "blur" && !inputs.hasBlurred.value) return "idle";
3930
+ return inputs.hasFinishedTyping.value ? validationState.value : "idle";
3931
+ });
4344
3932
  const errorMessage = (0, vue.computed)(() => {
3933
+ const ext = config.externalError();
3934
+ if (typeof ext === "string" && ext.length > 0) return ext;
4345
3935
  const v = validation.value;
4346
3936
  if (v.ok || !v.reason) return null;
4347
3937
  if (!inputs.phone.value) return null;
4348
3938
  return config.errorMessages()?.[v.reason] ?? inputs.messages.value.errorMessages[v.reason];
4349
3939
  });
4350
- const showError = (0, vue.computed)(() => Boolean(config.showValidation() && inputs.hasFinishedTyping.value && errorMessage.value));
3940
+ const showError = (0, vue.computed)(() => {
3941
+ if (!errorMessage.value) return false;
3942
+ if (externalErrorActive.value) return true;
3943
+ if (!config.showValidation()) return false;
3944
+ return visibleValidationState.value === "error";
3945
+ });
4351
3946
  return {
4352
3947
  validation,
4353
3948
  required,
@@ -4363,54 +3958,88 @@ function useTelInputValidation(deps, inputs, config) {
4363
3958
  };
4364
3959
  }
4365
3960
  //#endregion
4366
- //#region src/types.ts
4367
- const aTelInputVariants = (0, class_variance_authority.cva)("a-tel-input__field", {
4368
- variants: { size: {
4369
- xs: "",
4370
- sm: "",
4371
- md: "",
4372
- lg: "",
4373
- xl: ""
4374
- } },
4375
- defaultVariants: { size: "md" }
4376
- });
4377
- const DEFAULT_ERROR_MESSAGES = {
4378
- missing_country: "Please select a country.",
4379
- country_not_supported: "This country is not supported.",
4380
- phone_has_non_digits: "Phone number can only contain digits.",
4381
- too_short: "Phone number is too short.",
4382
- too_long: "Phone number is too long.",
4383
- invalid_phone: "Phone number is invalid.",
4384
- parse_failed: "Could not parse phone number."
4385
- };
4386
- /** English defaults for every {@link TelInputMessages} key. */
4387
- const DEFAULT_MESSAGES = {
4388
- searchPlaceholder: "Search country or +code…",
4389
- emptyText: "No countries found.",
4390
- loadingText: "Loading countries…",
4391
- suggestedLabel: "Suggested",
4392
- allCountriesLabel: "All countries",
4393
- errorMessages: DEFAULT_ERROR_MESSAGES,
4394
- countryLabel: "Country",
4395
- selectCountryLabel: "Select country",
4396
- phoneInputLabel: "Phone number"
4397
- };
3961
+ //#region src/composables/useCountrySelection.ts
4398
3962
  /**
4399
- * Merge a partial `messages` override onto the English defaults. Used internally by
4400
- * `ATelInput` to resolve a complete {@link TelInputMessages} object.
3963
+ * The picker selection state machine for {@link ATelInput}, consolidated into a
3964
+ * single composable so the component doesn't have to juggle three boolean flags
3965
+ * (`userPickedCountry` / `autoSettingCountry` / `inputDetectionApplied`) and
3966
+ * reason about their pairwise interactions.
3967
+ *
3968
+ * Every write to the selection goes through {@link UseCountrySelectionReturn.set},
3969
+ * which records both the new ISO2 and the *origin* of the change. That makes the
3970
+ * downstream decision — should detection re-route the picker on the next typed-input
3971
+ * burst? — a one-liner: `if (detectionLocked.value) return;`.
4401
3972
  */
4402
- function resolveMessages(input) {
4403
- if (!input) return DEFAULT_MESSAGES;
3973
+ function useCountrySelection() {
3974
+ const iso2 = (0, vue.ref)("");
3975
+ const source = (0, vue.ref)("none");
3976
+ function set(nextIso2, nextSource) {
3977
+ iso2.value = nextIso2;
3978
+ source.value = nextSource;
3979
+ }
3980
+ function clear() {
3981
+ iso2.value = "";
3982
+ source.value = "none";
3983
+ }
4404
3984
  return {
4405
- ...DEFAULT_MESSAGES,
4406
- ...input,
4407
- errorMessages: {
4408
- ...DEFAULT_ERROR_MESSAGES,
4409
- ...input.errorMessages
4410
- }
3985
+ iso2,
3986
+ source,
3987
+ set,
3988
+ clear,
3989
+ detectionLocked: (0, vue.computed)(() => source.value === "picker" || source.value === "input")
4411
3990
  };
4412
3991
  }
4413
3992
  //#endregion
3993
+ //#region src/composables/useSyncedModel.ts
3994
+ /**
3995
+ * Two-way bidirectional sync between a `defineModel` ref and internal component
3996
+ * state — with the **echo-loop guard** built in. Solves a recurring class of
3997
+ * bugs in this component where two watchers (external→internal and
3998
+ * internal→external) would fight each other and rewrite values the user just
3999
+ * typed.
4000
+ *
4001
+ * Mechanics:
4002
+ *
4003
+ * 1. When any of `triggers` change AND we're not currently applying an
4004
+ * external write, recompute the model value via `compose()` and write it
4005
+ * into `model`. Stamp `lastEmitted` first so we recognise the echo.
4006
+ * 2. When `model` changes AND the new value isn't the echo of our last emit,
4007
+ * apply it into internal state via `apply()`. The `applying` flag is held
4008
+ * for the duration of `apply()` so step (1) skips while we mutate.
4009
+ *
4010
+ * Used for:
4011
+ * - `modelValue` (E.164 string) ↔ `phone` + `selectedIso2`.
4012
+ * - `country` (dial-number) ↔ `selectedIso2`.
4013
+ *
4014
+ * The hand-rolled equivalents (`applyingModelValue` / `lastEmittedModelValue`
4015
+ * plus the country↔iso2 watcher pair with `autoSettingCountry`) collapse into
4016
+ * two calls to this helper.
4017
+ */
4018
+ function useSyncedModel(options) {
4019
+ const { model, triggers, compose, apply } = options;
4020
+ const isEqual = options.isEqual ?? Object.is;
4021
+ let applying = false;
4022
+ let lastEmitted = { __unset: true };
4023
+ const isEcho = (v) => typeof lastEmitted === "object" && lastEmitted !== null && "__unset" in lastEmitted ? false : isEqual(v, lastEmitted);
4024
+ (0, vue.watch)(model, (next) => {
4025
+ if (isEcho(next)) return;
4026
+ applying = true;
4027
+ try {
4028
+ apply(next);
4029
+ } finally {
4030
+ applying = false;
4031
+ }
4032
+ }, { immediate: true });
4033
+ (0, vue.watch)(triggers, () => {
4034
+ if (applying) return;
4035
+ const next = compose();
4036
+ if (!isEqual(next, model.value)) {
4037
+ lastEmitted = next;
4038
+ model.value = next;
4039
+ }
4040
+ }, { flush: "post" });
4041
+ }
4042
+ //#endregion
4414
4043
  //#region ../AResponsivePopover/dist/index.js
4415
4044
  var __defProp = Object.defineProperty;
4416
4045
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -17140,7 +16769,7 @@ const _sfc_main$9 = /* @__PURE__ */ (0, vue.defineComponent)({
17140
16769
  if (typeof document === "undefined") return [];
17141
16770
  return Array.from(document.querySelectorAll("[data-responsive-popover-scroll-container=\"true\"]"));
17142
16771
  },
17143
- active: (0, vue.computed)(() => !!ctx?.open.value && isDesktop.value && scrollLockMode.value === "events")
16772
+ active: (0, vue.computed)(() => !!ctx?.open.value && scrollLockMode.value === "events")
17144
16773
  });
17145
16774
  const __returned__ = {
17146
16775
  props,
@@ -17186,7 +16815,8 @@ function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
17186
16815
  ])) : ((0, vue.openBlock)(), (0, vue.createBlock)($setup["ADrawerContent"], {
17187
16816
  key: 1,
17188
16817
  class: (0, vue.normalizeClass)($setup.mergedClass),
17189
- "data-slot": "responsive-popover-content"
16818
+ "data-slot": "responsive-popover-content",
16819
+ "data-responsive-popover-scroll-container": "true"
17190
16820
  }, {
17191
16821
  default: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "default")]),
17192
16822
  _: 3
@@ -17597,11 +17227,11 @@ const _sfc_main$1 = /* @__PURE__ */ (0, vue.defineComponent)({
17597
17227
  setup(__props, { expose: __expose }) {
17598
17228
  const props = __props;
17599
17229
  const selected = (0, vue.useModel)(__props, "selected");
17600
- const { countries: internalCountries, isCountriesLoading, getCountries, searchCountries: defaultSearch, getCountryByValue: lookupInternal } = usePhoneValidation();
17230
+ const { countries: internalCountries, isCountriesLoading, getCountries, searchCountries: defaultSearch, getCountryByValue: lookupInternal } = require_usePhoneValidation.usePhoneValidation();
17601
17231
  const open = (0, vue.ref)(false);
17602
17232
  const search = (0, vue.ref)("");
17603
17233
  getCountries();
17604
- const effectiveCountries = (0, vue.computed)(() => props.countries && props.countries.length ? props.countries : localizeCountries(internalCountries.value, props.locale));
17234
+ const effectiveCountries = (0, vue.computed)(() => props.countries && props.countries.length ? props.countries : require_usePhoneValidation.localizeCountries(internalCountries.value, props.locale));
17605
17235
  const effectiveByValue = (0, vue.computed)(() => new Map(effectiveCountries.value.map((c) => [c.value, c])));
17606
17236
  function lookup(iso2) {
17607
17237
  if (!iso2) return null;
@@ -17845,7 +17475,7 @@ const _hoisted_6$1 = {
17845
17475
  };
17846
17476
  const _hoisted_7$1 = { class: "a-country-select__list" };
17847
17477
  const _hoisted_8$1 = { class: "a-country-select__loading" };
17848
- const _hoisted_9 = { class: "a-country-select__empty" };
17478
+ const _hoisted_9$1 = { class: "a-country-select__empty" };
17849
17479
  const _hoisted_10 = {
17850
17480
  key: 0,
17851
17481
  "data-slot": "country-select-group",
@@ -17943,7 +17573,7 @@ function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
17943
17573
  (0, vue.createElementVNode)("div", _hoisted_7$1, [$setup.isCountriesLoading && $setup.effectiveCountries.length === 0 ? (0, vue.renderSlot)(_ctx.$slots, "loading", { key: 0 }, () => [(0, vue.createElementVNode)("div", _hoisted_8$1, (0, vue.toDisplayString)($setup.props.loadingText), 1)], true) : $setup.isSearching && $setup.filtered.length === 0 ? (0, vue.renderSlot)(_ctx.$slots, "empty", {
17944
17574
  key: 1,
17945
17575
  query: $setup.search
17946
- }, () => [(0, vue.createElementVNode)("div", _hoisted_9, (0, vue.toDisplayString)($setup.props.emptyText), 1)], true) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [
17576
+ }, () => [(0, vue.createElementVNode)("div", _hoisted_9$1, (0, vue.toDisplayString)($setup.props.emptyText), 1)], true) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [
17947
17577
  (0, vue.createCommentVNode)(" Suggested group "),
17948
17578
  $setup.suggested.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("section", _hoisted_10, [(0, vue.renderSlot)(_ctx.$slots, "group-header", {
17949
17579
  label: $setup.props.suggestedLabel,
@@ -18072,6 +17702,27 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18072
17702
  required: false,
18073
17703
  skipCheck: true
18074
17704
  },
17705
+ modelValue: {
17706
+ type: String,
17707
+ required: false
17708
+ },
17709
+ name: {
17710
+ type: String,
17711
+ required: false
17712
+ },
17713
+ error: {
17714
+ type: [String, null],
17715
+ required: false
17716
+ },
17717
+ validating: {
17718
+ type: Boolean,
17719
+ required: false
17720
+ },
17721
+ validateOn: {
17722
+ type: String,
17723
+ required: false,
17724
+ default: "change"
17725
+ },
18075
17726
  placeholder: {
18076
17727
  type: String,
18077
17728
  required: false,
@@ -18214,28 +17865,65 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18214
17865
  type: [Number, null],
18215
17866
  default: null
18216
17867
  },
18217
- "countryModifiers": {}
17868
+ "countryModifiers": {},
17869
+ "modelValue": {
17870
+ type: String,
17871
+ default: ""
17872
+ },
17873
+ "modelModifiers": {}
18218
17874
  }),
18219
- emits: ["update:phone", "update:country"],
18220
- setup(__props, { expose: __expose }) {
17875
+ emits: /* @__PURE__ */ (0, vue.mergeModels)([
17876
+ "update:modelValue",
17877
+ "update:phone",
17878
+ "update:country",
17879
+ "blur",
17880
+ "focus"
17881
+ ], [
17882
+ "update:phone",
17883
+ "update:country",
17884
+ "update:modelValue"
17885
+ ]),
17886
+ setup(__props, { expose: __expose, emit: __emit }) {
18221
17887
  const props = __props;
17888
+ const emit = __emit;
18222
17889
  const phone = (0, vue.useModel)(__props, "phone");
18223
17890
  /** Public `v-model:country` — the **dial number** (e.g. `20` for Egypt, `44` for the UK,
18224
17891
  * `1` for the NANP block). `null` means no country selected. Internally the component
18225
17892
  * tracks a richer ISO2 code (`selectedIso2`) because dial codes alone can't disambiguate
18226
17893
  * NANP (`+1` covers 25+ countries) — the picker still needs an exact country. */
18227
17894
  const country = (0, vue.useModel)(__props, "country");
18228
- /** Internal source of truth — the ISO2 alpha-2 code of the picker selection. Synced with
18229
- * `country` (dial number) via watchers below. */
18230
- const selectedIso2 = (0, vue.ref)("");
18231
- const { getCountries, validate, getRequiredInfo, getCountryByValue, getCountriesByDial } = usePhoneValidation();
17895
+ /**
17896
+ * Default v-model the canonical **E.164** string (e.g. `'+201066105963'`).
17897
+ *
17898
+ * Single-string contract for VeeValidate's `<Field v-slot="{ field }">` pattern
17899
+ * (`v-bind="field"`), native `<form>` submission, or any `v-model="phoneE164"`
17900
+ * consumer. Bind it with:
17901
+ *
17902
+ * <ATelInput v-model="phoneE164" />
17903
+ *
17904
+ * <VeeField v-slot="{ field, errors }" name="phone">
17905
+ * <ATelInput v-bind="field" :error="errors[0]" />
17906
+ * </VeeField>
17907
+ *
17908
+ * When set externally, the value is parsed via libphonenumber-js → the country
17909
+ * picker and the digits-only `phone` model are derived from it. When the user
17910
+ * types or picks a country, the composed E.164 is written back out. Stays in
17911
+ * sync with `v-model:phone` / `v-model:country` — you can use either contract.
17912
+ */
17913
+ const modelValue = (0, vue.useModel)(__props, "modelValue");
17914
+ /** The picker selection state machine — `iso2` is the internal source of truth, `source`
17915
+ * records where the current selection came from, `detectionLocked` answers "should
17916
+ * typed-input detection re-route the picker on the next burst?". Single mutator: `set`.
17917
+ * Replaces the historical flag soup (`userPickedCountry` / `autoSettingCountry` /
17918
+ * `inputDetectionApplied`). */
17919
+ const selection = useCountrySelection();
17920
+ const selectedIso2 = selection.iso2;
17921
+ const { getCountries, validate, getRequiredInfo, getCountryByValue, getCountriesByDial } = require_usePhoneValidation.usePhoneValidation();
18232
17922
  const { resolveCountryIdentifier, dialNumberFor, matchLeadingDialCode } = useCountryMatching({
18233
17923
  getCountryByValue,
18234
17924
  getCountriesByDial
18235
17925
  });
18236
17926
  getCountries();
18237
- const userPickedCountry = (0, vue.ref)(false);
18238
- const autoSettingCountry = (0, vue.ref)(false);
18239
17927
  /** Silently resolved via IP/timezone/locale when `detectFromInput` is on — used as a hint
18240
17928
  * so local-format numbers (e.g. Egyptian `01066105963`) can be parsed without a `+` prefix.
18241
17929
  * Seeded from `defaultCountry` so it has a usable value before async detection resolves. */
@@ -18249,18 +17937,28 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18249
17937
  currentIso2: selectedIso2.value
18250
17938
  });
18251
17939
  }
17940
+ /** User explicitly picked a country from the picker — locks the selection so subsequent
17941
+ * typed-input detection cannot churn the picker. */
17942
+ function onPickerPick(iso2) {
17943
+ selection.set(iso2, "picker");
17944
+ }
18252
17945
  const typing = useTypingPhase({
18253
17946
  debounceMs: (0, vue.computed)(() => Math.max(0, props.detectDebounceMs)),
18254
17947
  onSettle: () => {
18255
17948
  if (!props.detectFromInput) return;
18256
- if (userPickedCountry.value || selectedIso2.value) return;
17949
+ if (selection.detectionLocked.value) return;
18257
17950
  const current = phone.value;
18258
17951
  if (!current) return;
17952
+ const typedInternational = (displayValue.value ?? "").trimStart().startsWith("+");
17953
+ if (selectedIso2.value && !typedInternational) return;
18259
17954
  typing.markDetectionAttempt();
18260
17955
  const match = tryMatchPhone(current);
18261
17956
  if (!match) return;
18262
- autoSettingCountry.value = true;
18263
- selectedIso2.value = match.country.value;
17957
+ if (match.country.value === selectedIso2.value && match.nationalNumber === phone.value) {
17958
+ selection.source.value = "input";
17959
+ return;
17960
+ }
17961
+ selection.set(match.country.value, "input");
18264
17962
  phone.value = match.nationalNumber;
18265
17963
  }
18266
17964
  });
@@ -18271,8 +17969,7 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18271
17969
  const seed = resolveCountryIdentifier(props.defaultCountry);
18272
17970
  if (seed) {
18273
17971
  inferredCountry.value = seed;
18274
- autoSettingCountry.value = true;
18275
- selectedIso2.value = seed;
17972
+ selection.set(seed, "default");
18276
17973
  return;
18277
17974
  }
18278
17975
  }
@@ -18291,43 +17988,31 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18291
17988
  const iso2 = detected ? detected.toUpperCase() : "";
18292
17989
  if (props.detectFromInput) {
18293
17990
  inferredCountry.value = iso2;
18294
- if (phone.value && !userPickedCountry.value && !selectedIso2.value) {
17991
+ if (phone.value && !selection.detectionLocked.value && !selectedIso2.value) {
18295
17992
  const match = tryMatchPhone(phone.value);
18296
17993
  if (match) {
18297
- autoSettingCountry.value = true;
18298
- selectedIso2.value = match.country.value;
17994
+ selection.set(match.country.value, "input");
18299
17995
  phone.value = match.nationalNumber;
18300
17996
  }
18301
17997
  }
18302
17998
  return;
18303
17999
  }
18304
- if (!selectedIso2.value && iso2) {
18305
- autoSettingCountry.value = true;
18306
- selectedIso2.value = iso2;
18307
- }
18000
+ if (!selectedIso2.value && iso2) selection.set(iso2, "env");
18308
18001
  });
18309
- /** External → internal: when the caller mutates `v-model:country` (dial number), resolve
18310
- * it to an ISO2. If the current ISO2 already maps to this dial (e.g. user has Canada
18311
- * selected and the caller writes back `1`), keep the existing selection — don't churn it. */
18312
- (0, vue.watch)(country, (next) => {
18313
- if (next == null) {
18314
- if (selectedIso2.value) selectedIso2.value = "";
18315
- return;
18002
+ useSyncedModel({
18003
+ model: country,
18004
+ triggers: [selectedIso2],
18005
+ compose: () => selectedIso2.value ? dialNumberFor(selectedIso2.value) : null,
18006
+ apply: (next) => {
18007
+ if (next == null) {
18008
+ selection.clear();
18009
+ return;
18010
+ }
18011
+ if (dialNumberFor(selectedIso2.value) === next) return;
18012
+ const iso2 = resolveCountryIdentifier(String(next));
18013
+ if (iso2) selection.set(iso2, "external");
18316
18014
  }
18317
- if (dialNumberFor(selectedIso2.value) === next) return;
18318
- const iso2 = resolveCountryIdentifier(String(next));
18319
- if (iso2) selectedIso2.value = iso2;
18320
- }, { immediate: true });
18321
- /** Internal → external: keep `country` (dial number) in lockstep with `selectedIso2`, and
18322
- * flag "user manually picked from picker" when the change isn't one we initiated.
18323
- * `flush: 'sync'` so the `autoSettingCountry` guard is reliable. */
18324
- (0, vue.watch)(selectedIso2, (iso2, prev) => {
18325
- const wasAutoSet = autoSettingCountry.value;
18326
- autoSettingCountry.value = false;
18327
- const nextDial = dialNumberFor(iso2);
18328
- if (country.value !== nextDial) country.value = nextDial;
18329
- if (!wasAutoSet && props.detectFromInput && iso2 && prev !== iso2) userPickedCountry.value = true;
18330
- }, { flush: "sync" });
18015
+ });
18331
18016
  /** The string shown in the `<input>`. Deliberately decoupled from `phone` (the digits-only
18332
18017
  * model) so the visible field is NOT rewritten mid-edit — non-digits / alternative numerals
18333
18018
  * are normalized into `phone` immediately, but the displayed value is only cleaned up once
@@ -18336,6 +18021,29 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18336
18021
  /** Set when the in-flight `phone` change came from the user typing — tells the `phone`
18337
18022
  * watcher to leave `displayValue` alone (the user is still editing it). */
18338
18023
  let phoneEditedByInput = false;
18024
+ useSyncedModel({
18025
+ model: modelValue,
18026
+ triggers: [phone, selectedIso2],
18027
+ compose: () => {
18028
+ if (!selectedIso2.value || !phone.value) return "";
18029
+ return validate({
18030
+ country: { iso2: selectedIso2.value },
18031
+ phone: phone.value
18032
+ }).full_phone ?? "";
18033
+ },
18034
+ apply: (next) => {
18035
+ const trimmed = String(next ?? "").trim();
18036
+ if (!trimmed) {
18037
+ if (phone.value !== "") phone.value = "";
18038
+ if (selectedIso2.value !== "") selection.clear();
18039
+ return;
18040
+ }
18041
+ const parsed = (0, libphonenumber_js.parsePhoneNumberFromString)(trimmed.startsWith("+") ? trimmed : `+${trimmed.replace(/^\+/, "")}`);
18042
+ if (!parsed || !parsed.country) return;
18043
+ if (selectedIso2.value !== parsed.country) selection.set(parsed.country, "external");
18044
+ if (phone.value !== parsed.nationalNumber) phone.value = parsed.nationalNumber;
18045
+ }
18046
+ });
18339
18047
  function commitPhone(value) {
18340
18048
  phoneEditedByInput = true;
18341
18049
  phone.value = value;
@@ -18343,14 +18051,10 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18343
18051
  function handlePhoneInput(e) {
18344
18052
  const target = e.target;
18345
18053
  displayValue.value = target.value;
18346
- const cleaned = normalizeDigits(target.value).replace(/\D/g, "");
18054
+ const cleaned = require_usePhoneValidation.normalizeDigits(target.value).replace(/\D/g, "");
18347
18055
  if (!cleaned) {
18348
18056
  typing.reset();
18349
- if (props.detectFromInput) {
18350
- autoSettingCountry.value = true;
18351
- selectedIso2.value = "";
18352
- userPickedCountry.value = false;
18353
- }
18057
+ if (props.detectFromInput) selection.clear();
18354
18058
  commitPhone("");
18355
18059
  return;
18356
18060
  }
@@ -18361,10 +18065,10 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18361
18065
  * value — fold alternative numerals to ASCII and drop any stray non-digits. */
18362
18066
  function handlePhoneChange(e) {
18363
18067
  const target = e.target;
18364
- displayValue.value = normalizeDigits(target.value).replace(/\D/g, "");
18068
+ displayValue.value = require_usePhoneValidation.normalizeDigits(target.value).replace(/\D/g, "");
18365
18069
  }
18366
18070
  (0, vue.watch)(() => phone.value, (next) => {
18367
- const cleaned = normalizeDigits(String(next ?? "")).replace(/\D/g, "");
18071
+ const cleaned = require_usePhoneValidation.normalizeDigits(String(next ?? "")).replace(/\D/g, "");
18368
18072
  if (cleaned !== next) {
18369
18073
  phone.value = cleaned;
18370
18074
  return;
@@ -18377,12 +18081,14 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18377
18081
  }, { flush: "post" });
18378
18082
  /** Resolved UI strings — `messages` prop merged onto English defaults. The individual
18379
18083
  * string props still win when both are set (see `errorMessage` / template bindings). */
18380
- const messages = (0, vue.computed)(() => resolveMessages(props.messages));
18084
+ const messages = (0, vue.computed)(() => require_types.resolveMessages(props.messages));
18381
18085
  /** `dir` of the outer wrapper — drives the hint/error text alignment and the country
18382
18086
  * picker popover. Explicit `'ltr'`/`'rtl'` is applied; `'auto'` or an omitted prop yields
18383
18087
  * `undefined` so it inherits from the page. The field row itself is always LTR so the
18384
18088
  * dial prefix / digits / flag trigger keep a consistent order. */
18385
18089
  const dirAttr = (0, vue.computed)(() => props.dir === "ltr" || props.dir === "rtl" ? props.dir : void 0);
18090
+ /** Set to `true` the first time the input is blurred. Drives `validateOn: 'blur'`. */
18091
+ const hasBlurred = (0, vue.ref)(false);
18386
18092
  const { validation, required, validationState, visibleValidationState, errorMessage, showError, showHint, selectedDialCode } = useTelInputValidation({
18387
18093
  validate,
18388
18094
  getRequiredInfo,
@@ -18391,15 +18097,35 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18391
18097
  phone,
18392
18098
  selectedIso2,
18393
18099
  hasFinishedTyping,
18100
+ hasBlurred,
18394
18101
  messages
18395
18102
  }, {
18396
18103
  locale: () => props.locale,
18397
18104
  showValidation: () => props.showValidation,
18398
- errorMessages: () => props.errorMessages
18105
+ errorMessages: () => props.errorMessages,
18106
+ validateOn: () => props.validateOn,
18107
+ externalError: () => props.error
18399
18108
  });
18400
18109
  const effectivePlaceholder = (0, vue.computed)(() => props.placeholder || required.value?.format_hint || messages.value.phoneInputLabel);
18401
18110
  const helperId = (0, vue.useId)();
18402
18111
  const describedBy = (0, vue.computed)(() => showError.value || showHint.value ? helperId : void 0);
18112
+ const inputRef = (0, vue.ref)(null);
18113
+ function handleBlur(e) {
18114
+ hasBlurred.value = true;
18115
+ emit("blur", e);
18116
+ }
18117
+ function handleFocus(e) {
18118
+ emit("focus", e);
18119
+ }
18120
+ function focus(options) {
18121
+ inputRef.value?.focus(options);
18122
+ }
18123
+ function blur() {
18124
+ inputRef.value?.blur();
18125
+ }
18126
+ function select() {
18127
+ inputRef.value?.select();
18128
+ }
18403
18129
  __expose({
18404
18130
  validation,
18405
18131
  required,
@@ -18408,12 +18134,18 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18408
18134
  visibleValidationState,
18409
18135
  isDetecting,
18410
18136
  hasFinishedTyping,
18411
- detectionAttempted
18137
+ detectionAttempted,
18138
+ focus,
18139
+ blur,
18140
+ select
18412
18141
  });
18413
18142
  const __returned__ = {
18414
18143
  props,
18144
+ emit,
18415
18145
  phone,
18416
18146
  country,
18147
+ modelValue,
18148
+ selection,
18417
18149
  selectedIso2,
18418
18150
  getCountries,
18419
18151
  validate,
@@ -18423,10 +18155,9 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18423
18155
  resolveCountryIdentifier,
18424
18156
  dialNumberFor,
18425
18157
  matchLeadingDialCode,
18426
- userPickedCountry,
18427
- autoSettingCountry,
18428
18158
  inferredCountry,
18429
18159
  tryMatchPhone,
18160
+ onPickerPick,
18430
18161
  typing,
18431
18162
  isDetecting,
18432
18163
  hasFinishedTyping,
@@ -18443,6 +18174,7 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18443
18174
  handlePhoneChange,
18444
18175
  messages,
18445
18176
  dirAttr,
18177
+ hasBlurred,
18446
18178
  validation,
18447
18179
  required,
18448
18180
  validationState,
@@ -18454,6 +18186,12 @@ const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
18454
18186
  effectivePlaceholder,
18455
18187
  helperId,
18456
18188
  describedBy,
18189
+ inputRef,
18190
+ handleBlur,
18191
+ handleFocus,
18192
+ focus,
18193
+ blur,
18194
+ select,
18457
18195
  get cn() {
18458
18196
  return cn$2;
18459
18197
  },
@@ -18495,25 +18233,34 @@ const _hoisted_4 = {
18495
18233
  };
18496
18234
  const _hoisted_5 = [
18497
18235
  "value",
18236
+ "name",
18498
18237
  "disabled",
18499
18238
  "placeholder",
18500
18239
  "aria-label",
18501
18240
  "aria-invalid",
18502
18241
  "aria-describedby",
18242
+ "aria-errormessage",
18243
+ "aria-busy",
18503
18244
  "data-has-dial"
18504
18245
  ];
18505
18246
  const _hoisted_6 = {
18247
+ key: 0,
18248
+ class: "a-tel-input__validating",
18249
+ "data-slot": "tel-input-validating",
18250
+ "aria-hidden": "true"
18251
+ };
18252
+ const _hoisted_7 = {
18506
18253
  key: 0,
18507
18254
  class: "a-tel-input__detecting",
18508
18255
  "aria-hidden": "true",
18509
18256
  "data-slot": "tel-input-detecting"
18510
18257
  };
18511
- const _hoisted_7 = {
18258
+ const _hoisted_8 = {
18512
18259
  key: 0,
18513
18260
  class: "a-tel-input__country-wrapper",
18514
18261
  "data-slot": "tel-input-country-wrapper"
18515
18262
  };
18516
- const _hoisted_8 = ["id"];
18263
+ const _hoisted_9 = ["id"];
18517
18264
  function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18518
18265
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
18519
18266
  class: (0, vue.normalizeClass)($setup.cn("a-tel-input", _ctx.$attrs.class)),
@@ -18531,31 +18278,41 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18531
18278
  (0, vue.renderSlot)(_ctx.$slots, "prefix", {}, void 0, true),
18532
18279
  $setup.selectedDialCode ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_4, (0, vue.toDisplayString)($setup.selectedDialCode), 1)) : (0, vue.createCommentVNode)("v-if", true),
18533
18280
  (0, vue.createElementVNode)("input", {
18281
+ ref: "inputRef",
18534
18282
  value: $setup.displayValue,
18535
18283
  type: "tel",
18536
18284
  inputmode: "numeric",
18537
18285
  autocomplete: "tel",
18538
18286
  dir: "ltr",
18539
18287
  "data-slot": "tel-input-field",
18288
+ name: $setup.props.name,
18540
18289
  disabled: $setup.props.disabled || $setup.props.loading,
18541
18290
  placeholder: $setup.effectivePlaceholder,
18542
18291
  "aria-label": $setup.messages.phoneInputLabel,
18543
18292
  "aria-invalid": $setup.visibleValidationState === "error" || void 0,
18544
18293
  "aria-describedby": $setup.describedBy,
18294
+ "aria-errormessage": $setup.visibleValidationState === "error" ? $setup.helperId : void 0,
18295
+ "aria-busy": $setup.props.validating || void 0,
18545
18296
  class: (0, vue.normalizeClass)($setup.cn("a-tel-input__input", $setup.props.inputClass)),
18546
18297
  "data-has-dial": $setup.selectedDialCode ? "" : void 0,
18547
18298
  onInput: $setup.handlePhoneInput,
18548
- onChange: $setup.handlePhoneChange
18299
+ onChange: $setup.handlePhoneChange,
18300
+ onBlur: $setup.handleBlur,
18301
+ onFocus: $setup.handleFocus
18549
18302
  }, null, 42, _hoisted_5),
18303
+ (0, vue.createCommentVNode)(" Async-validation spinner (e.g. server-side \"phone exists?\" check). Independent\n of `isDetecting` (which is for country detection) so both can be shown without\n interfering. Lives next to the input and never disables it. "),
18304
+ (0, vue.createVNode)(vue.Transition, { name: "a-tell-detect" }, {
18305
+ default: (0, vue.withCtx)(() => [$setup.props.validating ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6, [(0, vue.renderSlot)(_ctx.$slots, "validating", {}, () => [(0, vue.createVNode)($setup["SpinnerIcon"], { class: "a-tel-input__detecting-icon" })], true)])) : (0, vue.createCommentVNode)("v-if", true)]),
18306
+ _: 3
18307
+ }),
18550
18308
  (0, vue.createCommentVNode)(" Detection-in-flight spinner — shown only during the first debounce window,\n before the picker has appeared. Once the picker is visible (success OR a failed\n attempt that revealed the empty picker) we stop re-flashing on every keystroke. "),
18551
18309
  (0, vue.createVNode)(vue.Transition, { name: "a-tell-detect" }, {
18552
- default: (0, vue.withCtx)(() => [$setup.isDetecting && !$setup.selectedIso2 && !$setup.detectionAttempted ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6, [(0, vue.renderSlot)(_ctx.$slots, "detecting", {}, () => [(0, vue.createVNode)($setup["SpinnerIcon"], { class: "a-tel-input__detecting-icon" })], true)])) : (0, vue.createCommentVNode)("v-if", true)]),
18310
+ default: (0, vue.withCtx)(() => [$setup.isDetecting && !$setup.selectedIso2 && !$setup.detectionAttempted ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_7, [(0, vue.renderSlot)(_ctx.$slots, "detecting", {}, () => [(0, vue.createVNode)($setup["SpinnerIcon"], { class: "a-tel-input__detecting-icon" })], true)])) : (0, vue.createCommentVNode)("v-if", true)]),
18553
18311
  _: 3
18554
18312
  }),
18555
18313
  (0, vue.createVNode)(vue.Transition, { name: "a-tell-country" }, {
18556
- default: (0, vue.withCtx)(() => [!$setup.props.detectFromInput || $setup.selectedIso2 || $setup.detectionAttempted ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_7, [(0, vue.createVNode)($setup["ACountrySelect"], {
18314
+ default: (0, vue.withCtx)(() => [!$setup.props.detectFromInput || $setup.selectedIso2 || $setup.detectionAttempted ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_8, [(0, vue.createVNode)($setup["ACountrySelect"], {
18557
18315
  selected: $setup.selectedIso2,
18558
- "onUpdate:selected": _cache[0] || (_cache[0] = ($event) => $setup.selectedIso2 = $event),
18559
18316
  "allowed-dial-codes": $setup.props.allowedDialCodes,
18560
18317
  disabled: $setup.props.disabled || $setup.props.loading,
18561
18318
  size: $setup.props.size,
@@ -18566,6 +18323,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18566
18323
  "suggested-label": $setup.messages.suggestedLabel,
18567
18324
  "all-countries-label": $setup.messages.allCountriesLabel,
18568
18325
  "country-label": $setup.messages.countryLabel,
18326
+ "onUpdate:selected": $setup.onPickerPick,
18569
18327
  "select-country-label": $setup.messages.selectCountryLabel,
18570
18328
  "flag-url": $setup.props.flagUrl,
18571
18329
  searcher: $setup.props.searcher,
@@ -18672,7 +18430,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18672
18430
  }, () => [(0, vue.createElementVNode)("p", {
18673
18431
  "data-slot": "tel-input-hint",
18674
18432
  class: (0, vue.normalizeClass)($setup.cn("a-tel-input__hint", $setup.props.hintClass))
18675
- }, (0, vue.toDisplayString)($setup.required.format_hint), 3)], true) : (0, vue.createCommentVNode)("v-if", true)], 8, _hoisted_8)
18433
+ }, (0, vue.toDisplayString)($setup.required.format_hint), 3)], true) : (0, vue.createCommentVNode)("v-if", true)], 8, _hoisted_9)
18676
18434
  ], 10, _hoisted_1);
18677
18435
  }
18678
18436
  var ATelInput_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
@@ -18685,19 +18443,22 @@ exports.ACountryFlag = ACountryFlag_default;
18685
18443
  exports.ACountrySelect = ACountrySelect_default;
18686
18444
  exports.ATelInput = ATelInput_default;
18687
18445
  exports.COUNTRY_RECENTS_KEY = COUNTRY_RECENTS_KEY;
18688
- exports.DEFAULT_ERROR_MESSAGES = DEFAULT_ERROR_MESSAGES;
18689
- exports.DEFAULT_MESSAGES = DEFAULT_MESSAGES;
18446
+ exports.DEFAULT_ERROR_MESSAGES = require_types.DEFAULT_ERROR_MESSAGES;
18447
+ exports.DEFAULT_MESSAGES = require_types.DEFAULT_MESSAGES;
18690
18448
  exports.DIAL_TO_ISO2_FALLBACK = DIAL_TO_ISO2_FALLBACK;
18691
- exports.LOCALE_DIGIT_RANGES = LOCALE_DIGIT_RANGES;
18692
- exports.aTelInputVariants = aTelInputVariants;
18449
+ exports.FALLBACK_ISO2_LIST = FALLBACK_ISO2_LIST;
18450
+ exports.LOCALE_DIGIT_RANGES = require_usePhoneValidation.LOCALE_DIGIT_RANGES;
18451
+ exports.aTelInputVariants = require_types.aTelInputVariants;
18693
18452
  exports.defaultFlagUrl = defaultFlagUrl;
18694
18453
  exports.detectCountry = detectCountry;
18695
- exports.localizeCountries = localizeCountries;
18696
- exports.normalizeDigits = normalizeDigits;
18697
- exports.resolveMessages = resolveMessages;
18454
+ exports.localizeCountries = require_usePhoneValidation.localizeCountries;
18455
+ exports.normalizeDigits = require_usePhoneValidation.normalizeDigits;
18456
+ exports.resolveMessages = require_types.resolveMessages;
18698
18457
  exports.useCountryDetection = useCountryDetection;
18699
18458
  exports.useCountryMatching = useCountryMatching;
18700
- exports.usePhoneValidation = usePhoneValidation;
18459
+ exports.useCountrySelection = useCountrySelection;
18460
+ exports.usePhoneValidation = require_usePhoneValidation.usePhoneValidation;
18461
+ exports.useSyncedModel = useSyncedModel;
18701
18462
  exports.useTelInputValidation = useTelInputValidation;
18702
18463
  exports.useTypingPhase = useTypingPhase;
18703
18464