@alikhalilll/a-tel-input 1.0.2 → 1.1.1

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 (42) hide show
  1. package/.media/README.md +3 -0
  2. package/.media/hero.png +0 -0
  3. package/README.md +597 -72
  4. package/dist/_chunks/types.d.ts +661 -0
  5. package/dist/_chunks/types.js +52 -0
  6. package/dist/_chunks/types.js.map +1 -0
  7. package/dist/_chunks/usePhoneValidation.js +539 -0
  8. package/dist/_chunks/usePhoneValidation.js.map +1 -0
  9. package/dist/index.cjs +471 -695
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +122 -587
  12. package/dist/index.d.ts +122 -587
  13. package/dist/index.js +454 -658
  14. package/dist/index.js.map +1 -1
  15. package/dist/styles.css +20 -5
  16. package/dist/vee-validate/index.cjs +113 -0
  17. package/dist/vee-validate/index.cjs.map +1 -0
  18. package/dist/vee-validate/index.d.cts +86 -0
  19. package/dist/vee-validate/index.d.ts +86 -0
  20. package/dist/vee-validate/index.js +112 -0
  21. package/dist/vee-validate/index.js.map +1 -0
  22. package/dist/zod/index.cjs +211 -0
  23. package/dist/zod/index.cjs.map +1 -0
  24. package/dist/zod/index.d.cts +65 -0
  25. package/dist/zod/index.d.ts +65 -0
  26. package/dist/zod/index.js +208 -0
  27. package/dist/zod/index.js.map +1 -0
  28. package/package.json +34 -3
  29. package/src/components/ACountrySelect.vue +17 -3
  30. package/src/components/ATelInput.vue +206 -66
  31. package/src/composables/useCountryDetection.ts +28 -11
  32. package/src/composables/useCountryMatching.ts +160 -20
  33. package/src/composables/useCountrySelection.ts +71 -0
  34. package/src/composables/usePhoneValidation.ts +81 -18
  35. package/src/composables/useSyncedModel.ts +80 -0
  36. package/src/composables/useTelInputValidation.ts +50 -11
  37. package/src/index.ts +2 -0
  38. package/src/types.ts +80 -0
  39. package/src/vee-validate/index.ts +2 -0
  40. package/src/vee-validate/useTelField.ts +202 -0
  41. package/src/zod/index.ts +259 -0
  42. package/web-types.json +44 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
+ import { i as normalizeDigits, n as usePhoneValidation, r as LOCALE_DIGIT_RANGES, t as localizeCountries } from "./_chunks/usePhoneValidation.js";
2
+ import { i as resolveMessages, n as DEFAULT_MESSAGES, r as aTelInputVariants, t as DEFAULT_ERROR_MESSAGES } from "./_chunks/types.js";
1
3
  import * as vue from "vue";
2
4
  import { Comment, Fragment, Teleport, Transition, camelize, cloneVNode, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createVNode, defineComponent, getCurrentInstance, guardReactiveProps, h, inject, isRef, mergeDefaults, mergeModels, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onBeforeUnmount, onMounted, onUnmounted, onUpdated, openBlock, provide, reactive, readonly, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, toHandlerKey, toRef, toRefs, toValue, triggerRef, unref, useId, useModel, useSlots, vModelText, watch, watchEffect, watchPostEffect, withCtx, withDirectives, withModifiers } from "vue";
3
- import { getExampleNumber, isValidPhoneNumber, parsePhoneNumberFromString } from "libphonenumber-js";
4
- import examples from "libphonenumber-js/examples.mobile.json";
5
+ import { getCountries, parsePhoneNumberFromString } from "libphonenumber-js";
5
6
  import { computedEager, createGlobalState, createSharedComposable, defaultWindow, onKeyStroke, reactiveOmit, unrefElement, useDebounceFn, useEventListener, useMediaQuery, useMounted, useVModel } from "@vueuse/core";
6
- import { cva } from "class-variance-authority";
7
7
  import { hideOthers } from "aria-hidden";
8
8
  //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
9
9
  function r$2(e) {
@@ -3404,508 +3404,6 @@ function cn$2(...inputs) {
3404
3404
  return twMerge$2(clsx$2(inputs));
3405
3405
  }
3406
3406
  //#endregion
3407
- //#region src/utils/digits.ts
3408
- /**
3409
- * Alternative-numeral support. Phone numbers are routinely entered with the digits of the
3410
- * user's own script — Arabic-Indic, Persian/Urdu, Devanagari, Bengali. `libphonenumber-js`
3411
- * and our own `\d` cleanup only understand ASCII `0-9`, so anything else silently becomes
3412
- * an empty number. `normalizeDigits` folds those scripts down to ASCII before validation.
3413
- */
3414
- /**
3415
- * Base code points of contiguous decimal-digit blocks. Each block runs `base`‥`base+9`
3416
- * for digit `0`‥`9`, so the ASCII digit is `codePoint - base`. Add a script by appending
3417
- * one entry here.
3418
- */
3419
- const LOCALE_DIGIT_RANGES = [
3420
- {
3421
- name: "arabic-indic",
3422
- base: 1632
3423
- },
3424
- {
3425
- name: "extended-arabic",
3426
- base: 1776
3427
- },
3428
- {
3429
- name: "devanagari",
3430
- base: 2406
3431
- },
3432
- {
3433
- name: "bengali",
3434
- base: 2534
3435
- }
3436
- ];
3437
- /** Lookup of every non-ASCII digit code point → its ASCII character. */
3438
- const DIGIT_MAP = (() => {
3439
- const map = /* @__PURE__ */ new Map();
3440
- for (const { base } of LOCALE_DIGIT_RANGES) for (let d = 0; d <= 9; d++) map.set(base + d, String(d));
3441
- return map;
3442
- })();
3443
- /**
3444
- * Replace any supported non-ASCII decimal digit with its ASCII equivalent. Every other
3445
- * character (spaces, `+`, separators, letters) is left untouched — callers still run their
3446
- * own `\D` cleanup afterwards.
3447
- */
3448
- function normalizeDigits(input) {
3449
- const str = String(input ?? "");
3450
- let out = "";
3451
- for (const ch of str) {
3452
- const cp = ch.codePointAt(0);
3453
- out += cp != null && DIGIT_MAP.get(cp) || ch;
3454
- }
3455
- return out;
3456
- }
3457
- //#endregion
3458
- //#region src/composables/usePhoneValidation.ts
3459
- /**
3460
- * Country list + phone validation, framework-agnostic.
3461
- *
3462
- * Ported from the reference @pkgs/ui ATelInput composable with these cleanups:
3463
- * - Drop Nuxt-only `process.client` checks → use plain `typeof window !== 'undefined'`.
3464
- * - Drop Arabic default placeholder; let consumers pass their own.
3465
- * - Expand the offline fallback list from 2 → ~20 of the most-populated countries.
3466
- * - Keep REST Countries fetch + localStorage cache + libphonenumber-js examples + fast `search_key`.
3467
- */
3468
- const STORAGE_KEY = "ali_ui_phone_countries_v1";
3469
- const REST_COUNTRIES_URL = "https://restcountries.com/v3.1/all?fields=name,cca2,idd,flags";
3470
- const EX = examples;
3471
- const isBrowser$1 = () => typeof window !== "undefined";
3472
- function toDigits(v) {
3473
- return normalizeDigits(String(v ?? "")).replace(/\D/g, "");
3474
- }
3475
- /**
3476
- * Render an ASCII digit string in a locale's numeral system (e.g. `'ar'` → `٠-٩`).
3477
- * Used only for display hints — falls back to ASCII if the locale is unknown.
3478
- */
3479
- function localizeDigits(digits, locale) {
3480
- if (!locale) return digits;
3481
- try {
3482
- const fmt = new Intl.NumberFormat(locale, { useGrouping: false });
3483
- return digits.replace(/[0-9]/g, (d) => fmt.format(Number(d)));
3484
- } catch {
3485
- return digits;
3486
- }
3487
- }
3488
- function ensurePlusDial(dial) {
3489
- const d = toDigits(dial);
3490
- return d ? `+${d}` : "";
3491
- }
3492
- function normalizeIso2(iso2) {
3493
- return String(iso2 ?? "").trim().toUpperCase();
3494
- }
3495
- function dropLeadingZeros(digits) {
3496
- return String(digits ?? "").replace(/^0+/, "");
3497
- }
3498
- function buildFullE164(dial, digits) {
3499
- const dialClean = ensurePlusDial(dial);
3500
- const nsn = dropLeadingZeros(toDigits(digits));
3501
- return dialClean && nsn ? `${dialClean}${nsn}` : null;
3502
- }
3503
- function inferLengthFromExample(national) {
3504
- const d = toDigits(national);
3505
- if (!d) return {
3506
- min: null,
3507
- max: null
3508
- };
3509
- const n = d.length;
3510
- return {
3511
- min: Math.max(4, n - 2),
3512
- max: n + 2
3513
- };
3514
- }
3515
- function buildDialCode(idd) {
3516
- const root = idd?.root?.trim();
3517
- if (!root || !root.startsWith("+")) return null;
3518
- const out = `${root}${idd?.suffixes?.[0]?.trim() ?? ""}`;
3519
- return out.startsWith("+") ? out : null;
3520
- }
3521
- function normalizeSearchKey(input) {
3522
- return String(input ?? "").toLowerCase().replace(/\s+/g, " ").trim().replace(/[^\p{L}\p{N}+ ]/gu, "");
3523
- }
3524
- /**
3525
- * Return a copy of the country list with display names localized to `locale` via
3526
- * `Intl.DisplayNames`. `search_key` is rebuilt (keeping the English name too) so search
3527
- * still matches either spelling. Unknown locales / regions fall back to the English name.
3528
- */
3529
- function localizeCountries(list, locale) {
3530
- if (!locale) return list;
3531
- let display;
3532
- try {
3533
- display = new Intl.DisplayNames([locale], { type: "region" });
3534
- } catch {
3535
- return list;
3536
- }
3537
- return list.map((c) => {
3538
- let localized = c.raw_data.name;
3539
- try {
3540
- localized = display.of(c.raw_data.iso2) || c.raw_data.name;
3541
- } catch {}
3542
- if (localized === c.raw_data.name) return c;
3543
- const dial = c.raw_data.dial_code;
3544
- return {
3545
- ...c,
3546
- label: `${localized} (${dial})`,
3547
- search_key: normalizeSearchKey(`${localized} ${c.raw_data.name} ${dial} ${c.raw_data.iso2} ${c.raw_data.dial_digits}`),
3548
- raw_data: {
3549
- ...c.raw_data,
3550
- name: localized
3551
- }
3552
- };
3553
- });
3554
- }
3555
- function makeFallback(iso2, name, dial) {
3556
- const dialDigits = toDigits(dial);
3557
- return {
3558
- label: `${name} (+${dialDigits})`,
3559
- value: iso2,
3560
- search_key: normalizeSearchKey(`${name} +${dialDigits} ${iso2}`),
3561
- raw_data: {
3562
- iso2,
3563
- dial_code: `+${dialDigits}`,
3564
- dial_digits: dialDigits,
3565
- name,
3566
- flag: `https://flagcdn.com/w40/${iso2.toLowerCase()}.png`,
3567
- source: "fallback",
3568
- original: {}
3569
- }
3570
- };
3571
- }
3572
- const FALLBACK_COUNTRIES = [
3573
- makeFallback("SA", "Saudi Arabia", "+966"),
3574
- makeFallback("EG", "Egypt", "+20"),
3575
- makeFallback("AE", "United Arab Emirates", "+971"),
3576
- makeFallback("US", "United States", "+1"),
3577
- makeFallback("GB", "United Kingdom", "+44"),
3578
- makeFallback("DE", "Germany", "+49"),
3579
- makeFallback("FR", "France", "+33"),
3580
- makeFallback("ES", "Spain", "+34"),
3581
- makeFallback("IT", "Italy", "+39"),
3582
- makeFallback("TR", "Turkey", "+90"),
3583
- makeFallback("RU", "Russia", "+7"),
3584
- makeFallback("CN", "China", "+86"),
3585
- makeFallback("IN", "India", "+91"),
3586
- makeFallback("JP", "Japan", "+81"),
3587
- makeFallback("KR", "South Korea", "+82"),
3588
- makeFallback("BR", "Brazil", "+55"),
3589
- makeFallback("MX", "Mexico", "+52"),
3590
- makeFallback("CA", "Canada", "+1"),
3591
- makeFallback("AU", "Australia", "+61"),
3592
- makeFallback("NG", "Nigeria", "+234"),
3593
- makeFallback("PK", "Pakistan", "+92"),
3594
- makeFallback("ID", "Indonesia", "+62")
3595
- ];
3596
- function usePhoneValidation() {
3597
- const countries = ref([]);
3598
- const isCountriesLoading = ref(false);
3599
- const byValue = ref(/* @__PURE__ */ new Map());
3600
- const byDialDigits = ref(/* @__PURE__ */ new Map());
3601
- function rebuildIndexes(list) {
3602
- const valueMap = /* @__PURE__ */ new Map();
3603
- const dialMap = /* @__PURE__ */ new Map();
3604
- for (const item of list) {
3605
- valueMap.set(item.value, item);
3606
- const dial = item.raw_data.dial_digits;
3607
- if (dial) {
3608
- const bucket = dialMap.get(dial) ?? [];
3609
- bucket.push(item);
3610
- dialMap.set(dial, bucket);
3611
- }
3612
- }
3613
- byValue.value = valueMap;
3614
- byDialDigits.value = dialMap;
3615
- }
3616
- function upsertCountries(list) {
3617
- countries.value = list;
3618
- rebuildIndexes(list);
3619
- }
3620
- function normalizeRestCountries(list) {
3621
- const out = [];
3622
- for (const c of list) {
3623
- const name = c?.name?.common?.trim();
3624
- const iso2 = normalizeIso2(c?.cca2);
3625
- const dial = buildDialCode(c?.idd);
3626
- const flag = c?.flags?.png?.trim() || c?.flags?.svg?.trim() || null;
3627
- if (!name || !iso2 || !dial) continue;
3628
- const dialDigits = toDigits(dial);
3629
- const search_key = normalizeSearchKey(`${name} ${dial} ${iso2} ${dialDigits}`);
3630
- out.push({
3631
- label: `${name} (${dial})`,
3632
- value: iso2,
3633
- search_key,
3634
- raw_data: {
3635
- iso2,
3636
- dial_code: dial,
3637
- dial_digits: dialDigits,
3638
- name,
3639
- flag,
3640
- source: "restcountries",
3641
- original: c
3642
- }
3643
- });
3644
- }
3645
- const map = /* @__PURE__ */ new Map();
3646
- for (const item of out) {
3647
- const prev = map.get(item.value);
3648
- if (!prev) {
3649
- map.set(item.value, item);
3650
- continue;
3651
- }
3652
- const prevScore = (prev.raw_data.flag ? 1 : 0) + (prev.raw_data.dial_code ? 1 : 0);
3653
- if ((item.raw_data.flag ? 1 : 0) + (item.raw_data.dial_code ? 1 : 0) > prevScore) map.set(item.value, item);
3654
- }
3655
- return Array.from(map.values()).sort((a, b) => a.raw_data.name.localeCompare(b.raw_data.name));
3656
- }
3657
- async function getCountries(options) {
3658
- const force = Boolean(options?.force);
3659
- if (!force && countries.value.length) return countries.value;
3660
- if (!force && isBrowser$1()) try {
3661
- const cached = localStorage.getItem(STORAGE_KEY);
3662
- if (cached) {
3663
- const parsed = JSON.parse(cached);
3664
- if (Array.isArray(parsed) && parsed.length) {
3665
- upsertCountries(parsed);
3666
- return countries.value;
3667
- }
3668
- }
3669
- } catch {}
3670
- isCountriesLoading.value = true;
3671
- try {
3672
- const res = await fetch(REST_COUNTRIES_URL);
3673
- if (!res.ok) throw new Error(`Failed to fetch countries: ${res.status}`);
3674
- const normalized = normalizeRestCountries(await res.json());
3675
- upsertCountries(normalized.length ? normalized : FALLBACK_COUNTRIES);
3676
- if (isBrowser$1()) try {
3677
- localStorage.setItem(STORAGE_KEY, JSON.stringify(countries.value));
3678
- } catch {}
3679
- return countries.value;
3680
- } catch {
3681
- upsertCountries(FALLBACK_COUNTRIES);
3682
- return countries.value;
3683
- } finally {
3684
- isCountriesLoading.value = false;
3685
- }
3686
- }
3687
- function searchCountries(keyword, limit = 50) {
3688
- const q = normalizeSearchKey(keyword);
3689
- if (!q) return countries.value.slice(0, limit);
3690
- const res = [];
3691
- for (const item of countries.value) if (item.search_key.includes(q)) {
3692
- res.push(item);
3693
- if (res.length >= limit) break;
3694
- }
3695
- return res;
3696
- }
3697
- function getCountryByValue(value) {
3698
- return byValue.value.get(normalizeIso2(value)) ?? null;
3699
- }
3700
- function getCountriesByDial(dial) {
3701
- return byDialDigits.value.get(toDigits(dial)) ?? [];
3702
- }
3703
- function getRequiredInfo(country, locale) {
3704
- const iso2 = normalizeIso2(country.iso2);
3705
- if (!iso2) return null;
3706
- try {
3707
- const example = getExampleNumber(iso2, EX);
3708
- const exampleNational = example?.formatNational?.() ?? "";
3709
- const exampleE164 = example?.format?.("E.164") ?? "";
3710
- const inferred = inferLengthFromExample(exampleNational);
3711
- const dial_code = country.dial_code ? ensurePlusDial(country.dial_code) : exampleE164 ? `+${example?.countryCallingCode}` : "";
3712
- const digitsExample = toDigits(exampleNational);
3713
- return {
3714
- iso2,
3715
- dial_code,
3716
- placeholder: "",
3717
- example_national: exampleNational,
3718
- example_e164: exampleE164,
3719
- national_number_length: inferred,
3720
- format_hint: digitsExample ? `e.g. ${localizeDigits(digitsExample, locale)}` : ""
3721
- };
3722
- } catch {
3723
- return null;
3724
- }
3725
- }
3726
- function validate(input) {
3727
- const country = input.country ?? null;
3728
- if (!country?.iso2) return {
3729
- ok: false,
3730
- reason: "missing_country",
3731
- country: null,
3732
- phone: {
3733
- raw: ("phone" in input ? input.phone : null) ?? null,
3734
- digits: ""
3735
- },
3736
- full_phone: null,
3737
- required: null
3738
- };
3739
- const iso2 = normalizeIso2(country.iso2);
3740
- const required = getRequiredInfo({
3741
- iso2,
3742
- dial_code: country.dial_code
3743
- }, input.locale);
3744
- if (!required) return {
3745
- ok: false,
3746
- reason: "country_not_supported",
3747
- country: {
3748
- iso2,
3749
- dial_code: ensurePlusDial(country.dial_code)
3750
- },
3751
- phone: {
3752
- raw: ("phone" in input ? input.phone : null) ?? null,
3753
- digits: ""
3754
- },
3755
- full_phone: null,
3756
- required: null
3757
- };
3758
- if (!("phone" in input)) return {
3759
- ok: true,
3760
- reason: null,
3761
- country: {
3762
- iso2: required.iso2,
3763
- dial_code: required.dial_code
3764
- },
3765
- phone: {
3766
- raw: null,
3767
- digits: ""
3768
- },
3769
- full_phone: null,
3770
- required
3771
- };
3772
- const raw = input.phone;
3773
- const digits = toDigits(raw);
3774
- if (!raw || !String(raw).trim()) return {
3775
- ok: true,
3776
- reason: null,
3777
- country: {
3778
- iso2: required.iso2,
3779
- dial_code: required.dial_code
3780
- },
3781
- phone: {
3782
- raw: raw ?? null,
3783
- digits: ""
3784
- },
3785
- full_phone: null,
3786
- required
3787
- };
3788
- if (String(raw).replace(/\s+/g, "").match(/[^\d+]/)) return {
3789
- ok: false,
3790
- reason: "phone_has_non_digits",
3791
- country: {
3792
- iso2: required.iso2,
3793
- dial_code: required.dial_code
3794
- },
3795
- phone: {
3796
- raw,
3797
- digits
3798
- },
3799
- full_phone: buildFullE164(required.dial_code, digits),
3800
- required
3801
- };
3802
- const nsn = dropLeadingZeros(digits);
3803
- const { min, max } = required.national_number_length;
3804
- if (min !== null && nsn.length < min) return {
3805
- ok: false,
3806
- reason: "too_short",
3807
- country: {
3808
- iso2: required.iso2,
3809
- dial_code: required.dial_code
3810
- },
3811
- phone: {
3812
- raw,
3813
- digits
3814
- },
3815
- full_phone: buildFullE164(required.dial_code, digits),
3816
- required,
3817
- details: {
3818
- min,
3819
- actual: nsn.length
3820
- }
3821
- };
3822
- if (max !== null && nsn.length > max) return {
3823
- ok: false,
3824
- reason: "too_long",
3825
- country: {
3826
- iso2: required.iso2,
3827
- dial_code: required.dial_code
3828
- },
3829
- phone: {
3830
- raw,
3831
- digits
3832
- },
3833
- full_phone: buildFullE164(required.dial_code, digits),
3834
- required,
3835
- details: {
3836
- max,
3837
- actual: nsn.length
3838
- }
3839
- };
3840
- const full = buildFullE164(required.dial_code, digits) ?? String(raw);
3841
- try {
3842
- if (!isValidPhoneNumber(full, iso2)) {
3843
- const parsed = parsePhoneNumberFromString(full, iso2);
3844
- return {
3845
- ok: false,
3846
- reason: "invalid_phone",
3847
- country: {
3848
- iso2: required.iso2,
3849
- dial_code: required.dial_code
3850
- },
3851
- phone: {
3852
- raw,
3853
- digits
3854
- },
3855
- full_phone: parsed?.number ?? null,
3856
- required,
3857
- details: {
3858
- type: parsed?.getType?.() ?? null,
3859
- possible: parsed?.isPossible?.() ?? null,
3860
- country: parsed?.country ?? null
3861
- }
3862
- };
3863
- }
3864
- const parsed = parsePhoneNumberFromString(full, iso2);
3865
- return {
3866
- ok: true,
3867
- reason: null,
3868
- country: {
3869
- iso2: required.iso2,
3870
- dial_code: required.dial_code
3871
- },
3872
- phone: {
3873
- raw,
3874
- digits
3875
- },
3876
- full_phone: parsed?.number ?? full,
3877
- required
3878
- };
3879
- } catch (e) {
3880
- return {
3881
- ok: false,
3882
- reason: "parse_failed",
3883
- country: {
3884
- iso2: required.iso2,
3885
- dial_code: required.dial_code
3886
- },
3887
- phone: {
3888
- raw,
3889
- digits
3890
- },
3891
- full_phone: buildFullE164(required.dial_code, digits),
3892
- required,
3893
- details: { error: e?.message ?? String(e) }
3894
- };
3895
- }
3896
- }
3897
- return {
3898
- countries,
3899
- isCountriesLoading,
3900
- getCountries,
3901
- searchCountries,
3902
- getCountryByValue,
3903
- getCountriesByDial,
3904
- getRequiredInfo,
3905
- validate
3906
- };
3907
- }
3908
- //#endregion
3909
3407
  //#region src/composables/useCountryDetection.ts
3910
3408
  /**
3911
3409
  * Best-effort country detection chain: IP geolocation → timezone → navigator.language → fallback.
@@ -4015,24 +3513,32 @@ function tryLocale() {
4015
3513
  return null;
4016
3514
  }
4017
3515
  }
3516
+ const inflightIpFetch = /* @__PURE__ */ new Map();
4018
3517
  async function tryIp(endpoint, timeoutMs) {
4019
3518
  if (!isBrowser() || typeof fetch !== "function") return null;
3519
+ const existing = inflightIpFetch.get(endpoint);
3520
+ if (existing) return existing;
4020
3521
  const controller = new AbortController();
4021
3522
  const timer = setTimeout(() => controller.abort(), timeoutMs);
4022
- try {
4023
- const res = await fetch(endpoint, {
4024
- signal: controller.signal,
4025
- credentials: "omit"
4026
- });
4027
- if (!res.ok) return null;
4028
- const data = await res.json();
4029
- const code = (data.country_code ?? data.country ?? "").toString().toUpperCase();
4030
- return /^[A-Z]{2}$/.test(code) ? code : null;
4031
- } catch {
4032
- return null;
4033
- } finally {
4034
- clearTimeout(timer);
4035
- }
3523
+ const promise = (async () => {
3524
+ try {
3525
+ const res = await fetch(endpoint, {
3526
+ signal: controller.signal,
3527
+ credentials: "omit"
3528
+ });
3529
+ if (!res.ok) return null;
3530
+ const data = await res.json();
3531
+ const code = (data.country_code ?? data.country ?? "").toString().toUpperCase();
3532
+ return /^[A-Z]{2}$/.test(code) ? code : null;
3533
+ } catch {
3534
+ return null;
3535
+ } finally {
3536
+ clearTimeout(timer);
3537
+ inflightIpFetch.delete(endpoint);
3538
+ }
3539
+ })();
3540
+ inflightIpFetch.set(endpoint, promise);
3541
+ return promise;
4036
3542
  }
4037
3543
  function readCache() {
4038
3544
  if (!isBrowser()) return null;
@@ -4098,6 +3604,11 @@ function useCountryDetection(opts = {}) {
4098
3604
  }
4099
3605
  //#endregion
4100
3606
  //#region src/composables/useCountryMatching.ts
3607
+ /** Cached snapshot of every country libphonenumber knows about (~250 ISO2 codes).
3608
+ * Used by tier 2 of `matchLeadingDialCode` as the last-resort iteration so detection
3609
+ * works for *every* country, not just the popular ones in the bundled fallback list.
3610
+ * Cached at module load — `getCountries()` is a static metadata table, no I/O. */
3611
+ const ALL_LIBPHONENUMBER_ISO2 = getCountries();
4101
3612
  /** Synchronous dial-digit → ISO2 fallback for common countries, used when the async
4102
3613
  * REST Countries fetch hasn't populated `getCountriesByDial`'s index yet at setup. */
4103
3614
  const DIAL_TO_ISO2_FALLBACK = {
@@ -4164,6 +3675,58 @@ const DIAL_TO_ISO2_FALLBACK = {
4164
3675
  /** localStorage key for the user's most recently picked countries. Used as a
4165
3676
  * tie-breaker when multiple countries share a dial code (e.g. all NANP). */
4166
3677
  const COUNTRY_RECENTS_KEY = "ali_ui_country_recents_v1";
3678
+ /** ISO2 codes iterated by tier 2 of `matchLeadingDialCode` when looking for a country
3679
+ * that accepts a local-format input as valid. Mirrors the `FALLBACK_COUNTRIES` list in
3680
+ * {@link usePhoneValidation} (kept in sync by tests + by being short and obvious).
3681
+ * Order matters — earlier entries get priority when multiple countries would each
3682
+ * validate the same input. Built around the most-populated / most-likely countries. */
3683
+ const FALLBACK_ISO2_LIST = [
3684
+ "SA",
3685
+ "EG",
3686
+ "AE",
3687
+ "US",
3688
+ "GB",
3689
+ "DE",
3690
+ "FR",
3691
+ "ES",
3692
+ "IT",
3693
+ "TR",
3694
+ "RU",
3695
+ "CN",
3696
+ "IN",
3697
+ "JP",
3698
+ "KR",
3699
+ "BR",
3700
+ "MX",
3701
+ "CA",
3702
+ "AU",
3703
+ "NG",
3704
+ "PK",
3705
+ "ID"
3706
+ ];
3707
+ /** Build a minimal `CountryOption` from libphonenumber metadata when the async REST
3708
+ * Countries list hasn't loaded the entry yet. Used so country **detection** works
3709
+ * generically for any libphonenumber country, not just the ~22 in the offline
3710
+ * fallback list. The picker will overwrite this synthetic record with the real one
3711
+ * (with localized name + flag) as soon as `getCountries()` resolves. */
3712
+ function buildSyntheticCountry(iso2, dialDigits) {
3713
+ const ISO2 = iso2.toUpperCase();
3714
+ const digits = String(dialDigits).replace(/\D/g, "");
3715
+ return {
3716
+ label: `${ISO2} (+${digits})`,
3717
+ value: ISO2,
3718
+ search_key: `${ISO2.toLowerCase()} +${digits} ${digits}`,
3719
+ raw_data: {
3720
+ iso2: ISO2,
3721
+ dial_code: `+${digits}`,
3722
+ dial_digits: digits,
3723
+ name: ISO2,
3724
+ flag: `https://flagcdn.com/w40/${ISO2.toLowerCase()}.png`,
3725
+ source: "fallback",
3726
+ original: {}
3727
+ }
3728
+ };
3729
+ }
4167
3730
  function readRecents() {
4168
3731
  if (typeof window === "undefined") return [];
4169
3732
  try {
@@ -4209,38 +3772,75 @@ function useCountryMatching(deps) {
4209
3772
  const n = Number(digits);
4210
3773
  return Number.isFinite(n) ? n : null;
4211
3774
  }
3775
+ const MATCHER_CACHE_MAX = 128;
3776
+ const matcherCache = /* @__PURE__ */ new Map();
3777
+ function readMatcherCache(key) {
3778
+ if (!matcherCache.has(key)) return void 0;
3779
+ const value = matcherCache.get(key);
3780
+ matcherCache.delete(key);
3781
+ matcherCache.set(key, value);
3782
+ return value;
3783
+ }
3784
+ function writeMatcherCache(key, value) {
3785
+ if (matcherCache.size >= MATCHER_CACHE_MAX) {
3786
+ const oldest = matcherCache.keys().next().value;
3787
+ if (oldest !== void 0) matcherCache.delete(oldest);
3788
+ }
3789
+ matcherCache.set(key, value);
3790
+ }
4212
3791
  /** Three-tier match of the leading digits to a country:
4213
3792
  * 1. libphonenumber international parse (handles NANP disambiguation).
4214
- * 2. libphonenumber national-format parse using `hintCountry` (handles local
4215
- * formats like Egyptian `01066105963` with no dial-code prefix).
3793
+ * 2. libphonenumber national-format parse, iterating through candidate hint
3794
+ * countries (handles local formats like Egyptian `01066105963` with no
3795
+ * dial-code prefix). Universal coverage via `getCountries()`.
4216
3796
  * 3. Longest-prefix match against the dial-digits index, with the current
4217
- * selection / recents as tie-breakers when multiple countries share a code. */
3797
+ * selection / recents as tie-breakers when multiple countries share a code.
3798
+ *
3799
+ * Results are LRU-cached per input + context to avoid re-paying tier-2 iteration
3800
+ * cost when the user backspaces and retypes the same prefix. */
4218
3801
  function matchLeadingDialCode(digits, options = {}) {
4219
3802
  if (!digits) return null;
4220
3803
  const { hintCountry, currentIso2 } = options;
3804
+ const cacheKey = `${digits}|${hintCountry ?? ""}|${currentIso2 ?? ""}`;
3805
+ const cached = readMatcherCache(cacheKey);
3806
+ if (cached !== void 0) return cached;
3807
+ const result = runMatch(digits, hintCountry, currentIso2);
3808
+ writeMatcherCache(cacheKey, result);
3809
+ return result;
3810
+ }
3811
+ function runMatch(digits, hintCountry, currentIso2) {
4221
3812
  try {
4222
3813
  const parsed = parsePhoneNumberFromString(`+${digits}`);
4223
- if (parsed?.country && parsed.countryCallingCode) {
4224
- const parsedCountry = getCountryByValue(parsed.country);
4225
- if (parsedCountry) return {
4226
- country: parsedCountry,
4227
- nationalNumber: String(parsed.nationalNumber ?? "")
4228
- };
4229
- }
4230
- } catch {}
4231
- if (hintCountry && digits.length >= 4) try {
4232
- const parsed = parsePhoneNumberFromString(digits, hintCountry);
4233
- if (parsed?.isValid()) {
4234
- const matched = getCountryByValue(parsed.country || hintCountry);
4235
- if (matched) return {
4236
- country: matched,
4237
- nationalNumber: String(parsed.nationalNumber ?? "")
4238
- };
4239
- }
3814
+ if (parsed?.country && parsed.countryCallingCode) return {
3815
+ country: getCountryByValue(parsed.country) ?? buildSyntheticCountry(parsed.country, String(parsed.countryCallingCode)),
3816
+ nationalNumber: String(parsed.nationalNumber ?? "")
3817
+ };
4240
3818
  } catch {}
3819
+ if (digits.length >= 4) {
3820
+ const candidates = /* @__PURE__ */ new Set();
3821
+ if (hintCountry) candidates.add(hintCountry.toUpperCase());
3822
+ if (currentIso2) candidates.add(currentIso2.toUpperCase());
3823
+ for (const recent of readRecents()) candidates.add(recent.toUpperCase());
3824
+ for (const fallback of FALLBACK_ISO2_LIST) candidates.add(fallback);
3825
+ for (const all of ALL_LIBPHONENUMBER_ISO2) candidates.add(all);
3826
+ for (const iso2 of candidates) try {
3827
+ const parsed = parsePhoneNumberFromString(digits, iso2);
3828
+ if (parsed?.isValid()) {
3829
+ const resolvedIso2 = parsed.country || iso2;
3830
+ return {
3831
+ country: getCountryByValue(resolvedIso2) ?? buildSyntheticCountry(resolvedIso2, String(parsed.countryCallingCode ?? "")),
3832
+ nationalNumber: String(parsed.nationalNumber ?? "")
3833
+ };
3834
+ }
3835
+ } catch {}
3836
+ }
4241
3837
  for (let len = Math.min(3, digits.length); len >= 1; len--) {
4242
3838
  const prefix = digits.slice(0, len);
4243
- const group = getCountriesByDial(prefix);
3839
+ let group = getCountriesByDial(prefix);
3840
+ if (!group.length) {
3841
+ const iso2 = DIAL_TO_ISO2_FALLBACK[prefix];
3842
+ if (iso2) group = [getCountryByValue(iso2) ?? buildSyntheticCountry(iso2, prefix)];
3843
+ }
4244
3844
  if (!group.length) continue;
4245
3845
  const nationalNumber = digits.slice(prefix.length);
4246
3846
  if (group.length === 1) return {
@@ -4312,18 +3912,36 @@ function useTelInputValidation(deps, inputs, config) {
4312
3912
  phone: inputs.phone.value ?? "",
4313
3913
  locale: config.locale()
4314
3914
  }));
3915
+ const externalErrorActive = computed(() => {
3916
+ const e = config.externalError();
3917
+ return typeof e === "string" && e.length > 0;
3918
+ });
4315
3919
  const validationState = computed(() => {
3920
+ if (externalErrorActive.value) return "error";
4316
3921
  if (!inputs.phone.value) return "idle";
4317
3922
  return validation.value.ok ? "valid" : "error";
4318
3923
  });
4319
- const visibleValidationState = computed(() => inputs.hasFinishedTyping.value ? validationState.value : "idle");
3924
+ const visibleValidationState = computed(() => {
3925
+ if (externalErrorActive.value) return "error";
3926
+ const mode = config.validateOn() ?? "change";
3927
+ if (mode === "eager") return validationState.value;
3928
+ if (mode === "blur" && !inputs.hasBlurred.value) return "idle";
3929
+ return inputs.hasFinishedTyping.value ? validationState.value : "idle";
3930
+ });
4320
3931
  const errorMessage = computed(() => {
3932
+ const ext = config.externalError();
3933
+ if (typeof ext === "string" && ext.length > 0) return ext;
4321
3934
  const v = validation.value;
4322
3935
  if (v.ok || !v.reason) return null;
4323
3936
  if (!inputs.phone.value) return null;
4324
3937
  return config.errorMessages()?.[v.reason] ?? inputs.messages.value.errorMessages[v.reason];
4325
3938
  });
4326
- const showError = computed(() => Boolean(config.showValidation() && inputs.hasFinishedTyping.value && errorMessage.value));
3939
+ const showError = computed(() => {
3940
+ if (!errorMessage.value) return false;
3941
+ if (externalErrorActive.value) return true;
3942
+ if (!config.showValidation()) return false;
3943
+ return visibleValidationState.value === "error";
3944
+ });
4327
3945
  return {
4328
3946
  validation,
4329
3947
  required,
@@ -4339,54 +3957,88 @@ function useTelInputValidation(deps, inputs, config) {
4339
3957
  };
4340
3958
  }
4341
3959
  //#endregion
4342
- //#region src/types.ts
4343
- const aTelInputVariants = cva("a-tel-input__field", {
4344
- variants: { size: {
4345
- xs: "",
4346
- sm: "",
4347
- md: "",
4348
- lg: "",
4349
- xl: ""
4350
- } },
4351
- defaultVariants: { size: "md" }
4352
- });
4353
- const DEFAULT_ERROR_MESSAGES = {
4354
- missing_country: "Please select a country.",
4355
- country_not_supported: "This country is not supported.",
4356
- phone_has_non_digits: "Phone number can only contain digits.",
4357
- too_short: "Phone number is too short.",
4358
- too_long: "Phone number is too long.",
4359
- invalid_phone: "Phone number is invalid.",
4360
- parse_failed: "Could not parse phone number."
4361
- };
4362
- /** English defaults for every {@link TelInputMessages} key. */
4363
- const DEFAULT_MESSAGES = {
4364
- searchPlaceholder: "Search country or +code…",
4365
- emptyText: "No countries found.",
4366
- loadingText: "Loading countries…",
4367
- suggestedLabel: "Suggested",
4368
- allCountriesLabel: "All countries",
4369
- errorMessages: DEFAULT_ERROR_MESSAGES,
4370
- countryLabel: "Country",
4371
- selectCountryLabel: "Select country",
4372
- phoneInputLabel: "Phone number"
4373
- };
3960
+ //#region src/composables/useCountrySelection.ts
4374
3961
  /**
4375
- * Merge a partial `messages` override onto the English defaults. Used internally by
4376
- * `ATelInput` to resolve a complete {@link TelInputMessages} object.
3962
+ * The picker selection state machine for {@link ATelInput}, consolidated into a
3963
+ * single composable so the component doesn't have to juggle three boolean flags
3964
+ * (`userPickedCountry` / `autoSettingCountry` / `inputDetectionApplied`) and
3965
+ * reason about their pairwise interactions.
3966
+ *
3967
+ * Every write to the selection goes through {@link UseCountrySelectionReturn.set},
3968
+ * which records both the new ISO2 and the *origin* of the change. That makes the
3969
+ * downstream decision — should detection re-route the picker on the next typed-input
3970
+ * burst? — a one-liner: `if (detectionLocked.value) return;`.
4377
3971
  */
4378
- function resolveMessages(input) {
4379
- if (!input) return DEFAULT_MESSAGES;
3972
+ function useCountrySelection() {
3973
+ const iso2 = ref("");
3974
+ const source = ref("none");
3975
+ function set(nextIso2, nextSource) {
3976
+ iso2.value = nextIso2;
3977
+ source.value = nextSource;
3978
+ }
3979
+ function clear() {
3980
+ iso2.value = "";
3981
+ source.value = "none";
3982
+ }
4380
3983
  return {
4381
- ...DEFAULT_MESSAGES,
4382
- ...input,
4383
- errorMessages: {
4384
- ...DEFAULT_ERROR_MESSAGES,
4385
- ...input.errorMessages
4386
- }
3984
+ iso2,
3985
+ source,
3986
+ set,
3987
+ clear,
3988
+ detectionLocked: computed(() => source.value === "picker" || source.value === "input")
4387
3989
  };
4388
3990
  }
4389
3991
  //#endregion
3992
+ //#region src/composables/useSyncedModel.ts
3993
+ /**
3994
+ * Two-way bidirectional sync between a `defineModel` ref and internal component
3995
+ * state — with the **echo-loop guard** built in. Solves a recurring class of
3996
+ * bugs in this component where two watchers (external→internal and
3997
+ * internal→external) would fight each other and rewrite values the user just
3998
+ * typed.
3999
+ *
4000
+ * Mechanics:
4001
+ *
4002
+ * 1. When any of `triggers` change AND we're not currently applying an
4003
+ * external write, recompute the model value via `compose()` and write it
4004
+ * into `model`. Stamp `lastEmitted` first so we recognise the echo.
4005
+ * 2. When `model` changes AND the new value isn't the echo of our last emit,
4006
+ * apply it into internal state via `apply()`. The `applying` flag is held
4007
+ * for the duration of `apply()` so step (1) skips while we mutate.
4008
+ *
4009
+ * Used for:
4010
+ * - `modelValue` (E.164 string) ↔ `phone` + `selectedIso2`.
4011
+ * - `country` (dial-number) ↔ `selectedIso2`.
4012
+ *
4013
+ * The hand-rolled equivalents (`applyingModelValue` / `lastEmittedModelValue`
4014
+ * plus the country↔iso2 watcher pair with `autoSettingCountry`) collapse into
4015
+ * two calls to this helper.
4016
+ */
4017
+ function useSyncedModel(options) {
4018
+ const { model, triggers, compose, apply } = options;
4019
+ const isEqual = options.isEqual ?? Object.is;
4020
+ let applying = false;
4021
+ let lastEmitted = { __unset: true };
4022
+ const isEcho = (v) => typeof lastEmitted === "object" && lastEmitted !== null && "__unset" in lastEmitted ? false : isEqual(v, lastEmitted);
4023
+ watch(model, (next) => {
4024
+ if (isEcho(next)) return;
4025
+ applying = true;
4026
+ try {
4027
+ apply(next);
4028
+ } finally {
4029
+ applying = false;
4030
+ }
4031
+ }, { immediate: true });
4032
+ watch(triggers, () => {
4033
+ if (applying) return;
4034
+ const next = compose();
4035
+ if (!isEqual(next, model.value)) {
4036
+ lastEmitted = next;
4037
+ model.value = next;
4038
+ }
4039
+ }, { flush: "post" });
4040
+ }
4041
+ //#endregion
4390
4042
  //#region ../AResponsivePopover/dist/index.js
4391
4043
  var __defProp = Object.defineProperty;
4392
4044
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -16951,11 +16603,6 @@ const _sfc_main$2$2 = /* @__PURE__ */ defineComponent({
16951
16603
  const open = useModel(__props, "open");
16952
16604
  const isDesktop = useMediaQuery(() => props.breakpoint);
16953
16605
  /**
16954
- * Pre-imported on both branches — do NOT lazy-load. Switching the component identity at runtime
16955
- * means we still hydrate the right tree client-side.
16956
- */
16957
- const Root = computed(() => isDesktop.value ? APopover_default : ADrawer_default);
16958
- /**
16959
16606
  * Per-branch `modal` resolution — the two roots interpret the prop differently:
16960
16607
  *
16961
16608
  * APopover (desktop, reka-ui): `modal=true` triggers `PopoverContentModal` + its
@@ -16972,7 +16619,7 @@ const _sfc_main$2$2 = /* @__PURE__ */ defineComponent({
16972
16619
  return props.scrollLock === "body";
16973
16620
  });
16974
16621
  const drawerModal = computed(() => props.modal !== false);
16975
- const rootModal = computed(() => isDesktop.value ? rekaModal.value : drawerModal.value);
16622
+ const drawerNoBodyStyles = computed(() => props.scrollLock !== "body");
16976
16623
  provideResponsivePopoverContext({
16977
16624
  open: computed(() => open.value ?? false),
16978
16625
  isDesktop: computed(() => isDesktop.value),
@@ -16982,10 +16629,15 @@ const _sfc_main$2$2 = /* @__PURE__ */ defineComponent({
16982
16629
  props,
16983
16630
  open,
16984
16631
  isDesktop,
16985
- Root,
16986
16632
  rekaModal,
16987
16633
  drawerModal,
16988
- rootModal
16634
+ drawerNoBodyStyles,
16635
+ get APopover() {
16636
+ return APopover_default;
16637
+ },
16638
+ get ADrawer() {
16639
+ return ADrawer_default;
16640
+ }
16989
16641
  };
16990
16642
  Object.defineProperty(__returned__, "__isScriptSetup", {
16991
16643
  enumerable: false,
@@ -16995,15 +16647,30 @@ const _sfc_main$2$2 = /* @__PURE__ */ defineComponent({
16995
16647
  }
16996
16648
  });
16997
16649
  function _sfc_render$2$2(_ctx, _cache, $props, $setup, $data, $options) {
16998
- return openBlock(), createBlock(resolveDynamicComponent($setup.Root), {
16650
+ return $setup.isDesktop ? (openBlock(), createBlock($setup["APopover"], {
16651
+ key: 0,
16999
16652
  open: $setup.open,
17000
16653
  "onUpdate:open": _cache[0] || (_cache[0] = ($event) => $setup.open = $event),
17001
- modal: $setup.rootModal,
16654
+ modal: $setup.rekaModal,
17002
16655
  "data-slot": "responsive-popover"
17003
16656
  }, {
17004
- default: withCtx(() => [renderSlot(_ctx.$slots, "default", { isDesktop: $setup.isDesktop })]),
16657
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default", { isDesktop: true })]),
17005
16658
  _: 3
17006
- }, 40, ["open", "modal"]);
16659
+ }, 8, ["open", "modal"])) : (openBlock(), createBlock($setup["ADrawer"], {
16660
+ key: 1,
16661
+ open: $setup.open,
16662
+ "onUpdate:open": _cache[1] || (_cache[1] = ($event) => $setup.open = $event),
16663
+ modal: $setup.drawerModal,
16664
+ "no-body-styles": $setup.drawerNoBodyStyles,
16665
+ "data-slot": "responsive-popover"
16666
+ }, {
16667
+ default: withCtx(() => [renderSlot(_ctx.$slots, "default", { isDesktop: false })]),
16668
+ _: 3
16669
+ }, 8, [
16670
+ "open",
16671
+ "modal",
16672
+ "no-body-styles"
16673
+ ]));
17007
16674
  }
17008
16675
  var AResponsivePopover_default = /* @__PURE__ */ export_helper_default$3(_sfc_main$2$2, [["render", _sfc_render$2$2], ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/AResponsivePopover/src/components/AResponsivePopover.vue"]]);
17009
16676
  const _sfc_main$1$2 = /* @__PURE__ */ defineComponent({
@@ -17116,7 +16783,7 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
17116
16783
  if (typeof document === "undefined") return [];
17117
16784
  return Array.from(document.querySelectorAll("[data-responsive-popover-scroll-container=\"true\"]"));
17118
16785
  },
17119
- active: computed(() => !!ctx?.open.value && isDesktop.value && scrollLockMode.value === "events")
16786
+ active: computed(() => !!ctx?.open.value && scrollLockMode.value === "events")
17120
16787
  });
17121
16788
  const __returned__ = {
17122
16789
  props,
@@ -17162,7 +16829,8 @@ function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
17162
16829
  ])) : (openBlock(), createBlock($setup["ADrawerContent"], {
17163
16830
  key: 1,
17164
16831
  class: normalizeClass($setup.mergedClass),
17165
- "data-slot": "responsive-popover-content"
16832
+ "data-slot": "responsive-popover-content",
16833
+ "data-responsive-popover-scroll-container": "true"
17166
16834
  }, {
17167
16835
  default: withCtx(() => [renderSlot(_ctx.$slots, "default")]),
17168
16836
  _: 3
@@ -17821,7 +17489,7 @@ const _hoisted_6$1 = {
17821
17489
  };
17822
17490
  const _hoisted_7$1 = { class: "a-country-select__list" };
17823
17491
  const _hoisted_8$1 = { class: "a-country-select__loading" };
17824
- const _hoisted_9 = { class: "a-country-select__empty" };
17492
+ const _hoisted_9$1 = { class: "a-country-select__empty" };
17825
17493
  const _hoisted_10 = {
17826
17494
  key: 0,
17827
17495
  "data-slot": "country-select-group",
@@ -17919,7 +17587,7 @@ function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
17919
17587
  createElementVNode("div", _hoisted_7$1, [$setup.isCountriesLoading && $setup.effectiveCountries.length === 0 ? renderSlot(_ctx.$slots, "loading", { key: 0 }, () => [createElementVNode("div", _hoisted_8$1, toDisplayString($setup.props.loadingText), 1)], true) : $setup.isSearching && $setup.filtered.length === 0 ? renderSlot(_ctx.$slots, "empty", {
17920
17588
  key: 1,
17921
17589
  query: $setup.search
17922
- }, () => [createElementVNode("div", _hoisted_9, toDisplayString($setup.props.emptyText), 1)], true) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
17590
+ }, () => [createElementVNode("div", _hoisted_9$1, toDisplayString($setup.props.emptyText), 1)], true) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
17923
17591
  createCommentVNode(" Suggested group "),
17924
17592
  $setup.suggested.length > 0 ? (openBlock(), createElementBlock("section", _hoisted_10, [renderSlot(_ctx.$slots, "group-header", {
17925
17593
  label: $setup.props.suggestedLabel,
@@ -18048,6 +17716,27 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18048
17716
  required: false,
18049
17717
  skipCheck: true
18050
17718
  },
17719
+ modelValue: {
17720
+ type: String,
17721
+ required: false
17722
+ },
17723
+ name: {
17724
+ type: String,
17725
+ required: false
17726
+ },
17727
+ error: {
17728
+ type: [String, null],
17729
+ required: false
17730
+ },
17731
+ validating: {
17732
+ type: Boolean,
17733
+ required: false
17734
+ },
17735
+ validateOn: {
17736
+ type: String,
17737
+ required: false,
17738
+ default: "change"
17739
+ },
18051
17740
  placeholder: {
18052
17741
  type: String,
18053
17742
  required: false,
@@ -18190,28 +17879,65 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18190
17879
  type: [Number, null],
18191
17880
  default: null
18192
17881
  },
18193
- "countryModifiers": {}
17882
+ "countryModifiers": {},
17883
+ "modelValue": {
17884
+ type: String,
17885
+ default: ""
17886
+ },
17887
+ "modelModifiers": {}
18194
17888
  }),
18195
- emits: ["update:phone", "update:country"],
18196
- setup(__props, { expose: __expose }) {
17889
+ emits: /* @__PURE__ */ mergeModels([
17890
+ "update:modelValue",
17891
+ "update:phone",
17892
+ "update:country",
17893
+ "blur",
17894
+ "focus"
17895
+ ], [
17896
+ "update:phone",
17897
+ "update:country",
17898
+ "update:modelValue"
17899
+ ]),
17900
+ setup(__props, { expose: __expose, emit: __emit }) {
18197
17901
  const props = __props;
17902
+ const emit = __emit;
18198
17903
  const phone = useModel(__props, "phone");
18199
17904
  /** Public `v-model:country` — the **dial number** (e.g. `20` for Egypt, `44` for the UK,
18200
17905
  * `1` for the NANP block). `null` means no country selected. Internally the component
18201
17906
  * tracks a richer ISO2 code (`selectedIso2`) because dial codes alone can't disambiguate
18202
17907
  * NANP (`+1` covers 25+ countries) — the picker still needs an exact country. */
18203
17908
  const country = useModel(__props, "country");
18204
- /** Internal source of truth — the ISO2 alpha-2 code of the picker selection. Synced with
18205
- * `country` (dial number) via watchers below. */
18206
- const selectedIso2 = ref("");
17909
+ /**
17910
+ * Default v-model the canonical **E.164** string (e.g. `'+201066105963'`).
17911
+ *
17912
+ * Single-string contract for VeeValidate's `<Field v-slot="{ field }">` pattern
17913
+ * (`v-bind="field"`), native `<form>` submission, or any `v-model="phoneE164"`
17914
+ * consumer. Bind it with:
17915
+ *
17916
+ * <ATelInput v-model="phoneE164" />
17917
+ *
17918
+ * <VeeField v-slot="{ field, errors }" name="phone">
17919
+ * <ATelInput v-bind="field" :error="errors[0]" />
17920
+ * </VeeField>
17921
+ *
17922
+ * When set externally, the value is parsed via libphonenumber-js → the country
17923
+ * picker and the digits-only `phone` model are derived from it. When the user
17924
+ * types or picks a country, the composed E.164 is written back out. Stays in
17925
+ * sync with `v-model:phone` / `v-model:country` — you can use either contract.
17926
+ */
17927
+ const modelValue = useModel(__props, "modelValue");
17928
+ /** The picker selection state machine — `iso2` is the internal source of truth, `source`
17929
+ * records where the current selection came from, `detectionLocked` answers "should
17930
+ * typed-input detection re-route the picker on the next burst?". Single mutator: `set`.
17931
+ * Replaces the historical flag soup (`userPickedCountry` / `autoSettingCountry` /
17932
+ * `inputDetectionApplied`). */
17933
+ const selection = useCountrySelection();
17934
+ const selectedIso2 = selection.iso2;
18207
17935
  const { getCountries, validate, getRequiredInfo, getCountryByValue, getCountriesByDial } = usePhoneValidation();
18208
17936
  const { resolveCountryIdentifier, dialNumberFor, matchLeadingDialCode } = useCountryMatching({
18209
17937
  getCountryByValue,
18210
17938
  getCountriesByDial
18211
17939
  });
18212
17940
  getCountries();
18213
- const userPickedCountry = ref(false);
18214
- const autoSettingCountry = ref(false);
18215
17941
  /** Silently resolved via IP/timezone/locale when `detectFromInput` is on — used as a hint
18216
17942
  * so local-format numbers (e.g. Egyptian `01066105963`) can be parsed without a `+` prefix.
18217
17943
  * Seeded from `defaultCountry` so it has a usable value before async detection resolves. */
@@ -18225,18 +17951,28 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18225
17951
  currentIso2: selectedIso2.value
18226
17952
  });
18227
17953
  }
17954
+ /** User explicitly picked a country from the picker — locks the selection so subsequent
17955
+ * typed-input detection cannot churn the picker. */
17956
+ function onPickerPick(iso2) {
17957
+ selection.set(iso2, "picker");
17958
+ }
18228
17959
  const typing = useTypingPhase({
18229
17960
  debounceMs: computed(() => Math.max(0, props.detectDebounceMs)),
18230
17961
  onSettle: () => {
18231
17962
  if (!props.detectFromInput) return;
18232
- if (userPickedCountry.value || selectedIso2.value) return;
17963
+ if (selection.detectionLocked.value) return;
18233
17964
  const current = phone.value;
18234
17965
  if (!current) return;
17966
+ const typedInternational = (displayValue.value ?? "").trimStart().startsWith("+");
17967
+ if (selectedIso2.value && !typedInternational) return;
18235
17968
  typing.markDetectionAttempt();
18236
17969
  const match = tryMatchPhone(current);
18237
17970
  if (!match) return;
18238
- autoSettingCountry.value = true;
18239
- selectedIso2.value = match.country.value;
17971
+ if (match.country.value === selectedIso2.value && match.nationalNumber === phone.value) {
17972
+ selection.source.value = "input";
17973
+ return;
17974
+ }
17975
+ selection.set(match.country.value, "input");
18240
17976
  phone.value = match.nationalNumber;
18241
17977
  }
18242
17978
  });
@@ -18247,8 +17983,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18247
17983
  const seed = resolveCountryIdentifier(props.defaultCountry);
18248
17984
  if (seed) {
18249
17985
  inferredCountry.value = seed;
18250
- autoSettingCountry.value = true;
18251
- selectedIso2.value = seed;
17986
+ selection.set(seed, "default");
18252
17987
  return;
18253
17988
  }
18254
17989
  }
@@ -18267,43 +18002,31 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18267
18002
  const iso2 = detected ? detected.toUpperCase() : "";
18268
18003
  if (props.detectFromInput) {
18269
18004
  inferredCountry.value = iso2;
18270
- if (phone.value && !userPickedCountry.value && !selectedIso2.value) {
18005
+ if (phone.value && !selection.detectionLocked.value && !selectedIso2.value) {
18271
18006
  const match = tryMatchPhone(phone.value);
18272
18007
  if (match) {
18273
- autoSettingCountry.value = true;
18274
- selectedIso2.value = match.country.value;
18008
+ selection.set(match.country.value, "input");
18275
18009
  phone.value = match.nationalNumber;
18276
18010
  }
18277
18011
  }
18278
18012
  return;
18279
18013
  }
18280
- if (!selectedIso2.value && iso2) {
18281
- autoSettingCountry.value = true;
18282
- selectedIso2.value = iso2;
18283
- }
18014
+ if (!selectedIso2.value && iso2) selection.set(iso2, "env");
18284
18015
  });
18285
- /** External → internal: when the caller mutates `v-model:country` (dial number), resolve
18286
- * it to an ISO2. If the current ISO2 already maps to this dial (e.g. user has Canada
18287
- * selected and the caller writes back `1`), keep the existing selection — don't churn it. */
18288
- watch(country, (next) => {
18289
- if (next == null) {
18290
- if (selectedIso2.value) selectedIso2.value = "";
18291
- return;
18016
+ useSyncedModel({
18017
+ model: country,
18018
+ triggers: [selectedIso2],
18019
+ compose: () => selectedIso2.value ? dialNumberFor(selectedIso2.value) : null,
18020
+ apply: (next) => {
18021
+ if (next == null) {
18022
+ selection.clear();
18023
+ return;
18024
+ }
18025
+ if (dialNumberFor(selectedIso2.value) === next) return;
18026
+ const iso2 = resolveCountryIdentifier(String(next));
18027
+ if (iso2) selection.set(iso2, "external");
18292
18028
  }
18293
- if (dialNumberFor(selectedIso2.value) === next) return;
18294
- const iso2 = resolveCountryIdentifier(String(next));
18295
- if (iso2) selectedIso2.value = iso2;
18296
- }, { immediate: true });
18297
- /** Internal → external: keep `country` (dial number) in lockstep with `selectedIso2`, and
18298
- * flag "user manually picked from picker" when the change isn't one we initiated.
18299
- * `flush: 'sync'` so the `autoSettingCountry` guard is reliable. */
18300
- watch(selectedIso2, (iso2, prev) => {
18301
- const wasAutoSet = autoSettingCountry.value;
18302
- autoSettingCountry.value = false;
18303
- const nextDial = dialNumberFor(iso2);
18304
- if (country.value !== nextDial) country.value = nextDial;
18305
- if (!wasAutoSet && props.detectFromInput && iso2 && prev !== iso2) userPickedCountry.value = true;
18306
- }, { flush: "sync" });
18029
+ });
18307
18030
  /** The string shown in the `<input>`. Deliberately decoupled from `phone` (the digits-only
18308
18031
  * model) so the visible field is NOT rewritten mid-edit — non-digits / alternative numerals
18309
18032
  * are normalized into `phone` immediately, but the displayed value is only cleaned up once
@@ -18312,6 +18035,29 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18312
18035
  /** Set when the in-flight `phone` change came from the user typing — tells the `phone`
18313
18036
  * watcher to leave `displayValue` alone (the user is still editing it). */
18314
18037
  let phoneEditedByInput = false;
18038
+ useSyncedModel({
18039
+ model: modelValue,
18040
+ triggers: [phone, selectedIso2],
18041
+ compose: () => {
18042
+ if (!selectedIso2.value || !phone.value) return "";
18043
+ return validate({
18044
+ country: { iso2: selectedIso2.value },
18045
+ phone: phone.value
18046
+ }).full_phone ?? "";
18047
+ },
18048
+ apply: (next) => {
18049
+ const trimmed = String(next ?? "").trim();
18050
+ if (!trimmed) {
18051
+ if (phone.value !== "") phone.value = "";
18052
+ if (selectedIso2.value !== "") selection.clear();
18053
+ return;
18054
+ }
18055
+ const parsed = parsePhoneNumberFromString(trimmed.startsWith("+") ? trimmed : `+${trimmed.replace(/^\+/, "")}`);
18056
+ if (!parsed || !parsed.country) return;
18057
+ if (selectedIso2.value !== parsed.country) selection.set(parsed.country, "external");
18058
+ if (phone.value !== parsed.nationalNumber) phone.value = parsed.nationalNumber;
18059
+ }
18060
+ });
18315
18061
  function commitPhone(value) {
18316
18062
  phoneEditedByInput = true;
18317
18063
  phone.value = value;
@@ -18322,11 +18068,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18322
18068
  const cleaned = normalizeDigits(target.value).replace(/\D/g, "");
18323
18069
  if (!cleaned) {
18324
18070
  typing.reset();
18325
- if (props.detectFromInput) {
18326
- autoSettingCountry.value = true;
18327
- selectedIso2.value = "";
18328
- userPickedCountry.value = false;
18329
- }
18071
+ if (props.detectFromInput) selection.clear();
18330
18072
  commitPhone("");
18331
18073
  return;
18332
18074
  }
@@ -18359,6 +18101,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18359
18101
  * `undefined` so it inherits from the page. The field row itself is always LTR so the
18360
18102
  * dial prefix / digits / flag trigger keep a consistent order. */
18361
18103
  const dirAttr = computed(() => props.dir === "ltr" || props.dir === "rtl" ? props.dir : void 0);
18104
+ /** Set to `true` the first time the input is blurred. Drives `validateOn: 'blur'`. */
18105
+ const hasBlurred = ref(false);
18362
18106
  const { validation, required, validationState, visibleValidationState, errorMessage, showError, showHint, selectedDialCode } = useTelInputValidation({
18363
18107
  validate,
18364
18108
  getRequiredInfo,
@@ -18367,15 +18111,35 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18367
18111
  phone,
18368
18112
  selectedIso2,
18369
18113
  hasFinishedTyping,
18114
+ hasBlurred,
18370
18115
  messages
18371
18116
  }, {
18372
18117
  locale: () => props.locale,
18373
18118
  showValidation: () => props.showValidation,
18374
- errorMessages: () => props.errorMessages
18119
+ errorMessages: () => props.errorMessages,
18120
+ validateOn: () => props.validateOn,
18121
+ externalError: () => props.error
18375
18122
  });
18376
18123
  const effectivePlaceholder = computed(() => props.placeholder || required.value?.format_hint || messages.value.phoneInputLabel);
18377
18124
  const helperId = useId();
18378
18125
  const describedBy = computed(() => showError.value || showHint.value ? helperId : void 0);
18126
+ const inputRef = ref(null);
18127
+ function handleBlur(e) {
18128
+ hasBlurred.value = true;
18129
+ emit("blur", e);
18130
+ }
18131
+ function handleFocus(e) {
18132
+ emit("focus", e);
18133
+ }
18134
+ function focus(options) {
18135
+ inputRef.value?.focus(options);
18136
+ }
18137
+ function blur() {
18138
+ inputRef.value?.blur();
18139
+ }
18140
+ function select() {
18141
+ inputRef.value?.select();
18142
+ }
18379
18143
  __expose({
18380
18144
  validation,
18381
18145
  required,
@@ -18384,12 +18148,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18384
18148
  visibleValidationState,
18385
18149
  isDetecting,
18386
18150
  hasFinishedTyping,
18387
- detectionAttempted
18151
+ detectionAttempted,
18152
+ focus,
18153
+ blur,
18154
+ select
18388
18155
  });
18389
18156
  const __returned__ = {
18390
18157
  props,
18158
+ emit,
18391
18159
  phone,
18392
18160
  country,
18161
+ modelValue,
18162
+ selection,
18393
18163
  selectedIso2,
18394
18164
  getCountries,
18395
18165
  validate,
@@ -18399,10 +18169,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18399
18169
  resolveCountryIdentifier,
18400
18170
  dialNumberFor,
18401
18171
  matchLeadingDialCode,
18402
- userPickedCountry,
18403
- autoSettingCountry,
18404
18172
  inferredCountry,
18405
18173
  tryMatchPhone,
18174
+ onPickerPick,
18406
18175
  typing,
18407
18176
  isDetecting,
18408
18177
  hasFinishedTyping,
@@ -18419,6 +18188,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18419
18188
  handlePhoneChange,
18420
18189
  messages,
18421
18190
  dirAttr,
18191
+ hasBlurred,
18422
18192
  validation,
18423
18193
  required,
18424
18194
  validationState,
@@ -18430,6 +18200,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
18430
18200
  effectivePlaceholder,
18431
18201
  helperId,
18432
18202
  describedBy,
18203
+ inputRef,
18204
+ handleBlur,
18205
+ handleFocus,
18206
+ focus,
18207
+ blur,
18208
+ select,
18433
18209
  get cn() {
18434
18210
  return cn$2;
18435
18211
  },
@@ -18471,25 +18247,34 @@ const _hoisted_4 = {
18471
18247
  };
18472
18248
  const _hoisted_5 = [
18473
18249
  "value",
18250
+ "name",
18474
18251
  "disabled",
18475
18252
  "placeholder",
18476
18253
  "aria-label",
18477
18254
  "aria-invalid",
18478
18255
  "aria-describedby",
18256
+ "aria-errormessage",
18257
+ "aria-busy",
18479
18258
  "data-has-dial"
18480
18259
  ];
18481
18260
  const _hoisted_6 = {
18261
+ key: 0,
18262
+ class: "a-tel-input__validating",
18263
+ "data-slot": "tel-input-validating",
18264
+ "aria-hidden": "true"
18265
+ };
18266
+ const _hoisted_7 = {
18482
18267
  key: 0,
18483
18268
  class: "a-tel-input__detecting",
18484
18269
  "aria-hidden": "true",
18485
18270
  "data-slot": "tel-input-detecting"
18486
18271
  };
18487
- const _hoisted_7 = {
18272
+ const _hoisted_8 = {
18488
18273
  key: 0,
18489
18274
  class: "a-tel-input__country-wrapper",
18490
18275
  "data-slot": "tel-input-country-wrapper"
18491
18276
  };
18492
- const _hoisted_8 = ["id"];
18277
+ const _hoisted_9 = ["id"];
18493
18278
  function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18494
18279
  return openBlock(), createElementBlock("div", {
18495
18280
  class: normalizeClass($setup.cn("a-tel-input", _ctx.$attrs.class)),
@@ -18507,31 +18292,41 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18507
18292
  renderSlot(_ctx.$slots, "prefix", {}, void 0, true),
18508
18293
  $setup.selectedDialCode ? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString($setup.selectedDialCode), 1)) : createCommentVNode("v-if", true),
18509
18294
  createElementVNode("input", {
18295
+ ref: "inputRef",
18510
18296
  value: $setup.displayValue,
18511
18297
  type: "tel",
18512
18298
  inputmode: "numeric",
18513
18299
  autocomplete: "tel",
18514
18300
  dir: "ltr",
18515
18301
  "data-slot": "tel-input-field",
18302
+ name: $setup.props.name,
18516
18303
  disabled: $setup.props.disabled || $setup.props.loading,
18517
18304
  placeholder: $setup.effectivePlaceholder,
18518
18305
  "aria-label": $setup.messages.phoneInputLabel,
18519
18306
  "aria-invalid": $setup.visibleValidationState === "error" || void 0,
18520
18307
  "aria-describedby": $setup.describedBy,
18308
+ "aria-errormessage": $setup.visibleValidationState === "error" ? $setup.helperId : void 0,
18309
+ "aria-busy": $setup.props.validating || void 0,
18521
18310
  class: normalizeClass($setup.cn("a-tel-input__input", $setup.props.inputClass)),
18522
18311
  "data-has-dial": $setup.selectedDialCode ? "" : void 0,
18523
18312
  onInput: $setup.handlePhoneInput,
18524
- onChange: $setup.handlePhoneChange
18313
+ onChange: $setup.handlePhoneChange,
18314
+ onBlur: $setup.handleBlur,
18315
+ onFocus: $setup.handleFocus
18525
18316
  }, null, 42, _hoisted_5),
18317
+ 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. "),
18318
+ createVNode(Transition, { name: "a-tell-detect" }, {
18319
+ default: withCtx(() => [$setup.props.validating ? (openBlock(), createElementBlock("div", _hoisted_6, [renderSlot(_ctx.$slots, "validating", {}, () => [createVNode($setup["SpinnerIcon"], { class: "a-tel-input__detecting-icon" })], true)])) : createCommentVNode("v-if", true)]),
18320
+ _: 3
18321
+ }),
18526
18322
  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. "),
18527
18323
  createVNode(Transition, { name: "a-tell-detect" }, {
18528
- default: withCtx(() => [$setup.isDetecting && !$setup.selectedIso2 && !$setup.detectionAttempted ? (openBlock(), createElementBlock("div", _hoisted_6, [renderSlot(_ctx.$slots, "detecting", {}, () => [createVNode($setup["SpinnerIcon"], { class: "a-tel-input__detecting-icon" })], true)])) : createCommentVNode("v-if", true)]),
18324
+ default: withCtx(() => [$setup.isDetecting && !$setup.selectedIso2 && !$setup.detectionAttempted ? (openBlock(), createElementBlock("div", _hoisted_7, [renderSlot(_ctx.$slots, "detecting", {}, () => [createVNode($setup["SpinnerIcon"], { class: "a-tel-input__detecting-icon" })], true)])) : createCommentVNode("v-if", true)]),
18529
18325
  _: 3
18530
18326
  }),
18531
18327
  createVNode(Transition, { name: "a-tell-country" }, {
18532
- default: withCtx(() => [!$setup.props.detectFromInput || $setup.selectedIso2 || $setup.detectionAttempted ? (openBlock(), createElementBlock("div", _hoisted_7, [createVNode($setup["ACountrySelect"], {
18328
+ default: withCtx(() => [!$setup.props.detectFromInput || $setup.selectedIso2 || $setup.detectionAttempted ? (openBlock(), createElementBlock("div", _hoisted_8, [createVNode($setup["ACountrySelect"], {
18533
18329
  selected: $setup.selectedIso2,
18534
- "onUpdate:selected": _cache[0] || (_cache[0] = ($event) => $setup.selectedIso2 = $event),
18535
18330
  "allowed-dial-codes": $setup.props.allowedDialCodes,
18536
18331
  disabled: $setup.props.disabled || $setup.props.loading,
18537
18332
  size: $setup.props.size,
@@ -18542,6 +18337,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18542
18337
  "suggested-label": $setup.messages.suggestedLabel,
18543
18338
  "all-countries-label": $setup.messages.allCountriesLabel,
18544
18339
  "country-label": $setup.messages.countryLabel,
18340
+ "onUpdate:selected": $setup.onPickerPick,
18545
18341
  "select-country-label": $setup.messages.selectCountryLabel,
18546
18342
  "flag-url": $setup.props.flagUrl,
18547
18343
  searcher: $setup.props.searcher,
@@ -18648,7 +18444,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
18648
18444
  }, () => [createElementVNode("p", {
18649
18445
  "data-slot": "tel-input-hint",
18650
18446
  class: normalizeClass($setup.cn("a-tel-input__hint", $setup.props.hintClass))
18651
- }, toDisplayString($setup.required.format_hint), 3)], true) : createCommentVNode("v-if", true)], 8, _hoisted_8)
18447
+ }, toDisplayString($setup.required.format_hint), 3)], true) : createCommentVNode("v-if", true)], 8, _hoisted_9)
18652
18448
  ], 10, _hoisted_1);
18653
18449
  }
18654
18450
  var ATelInput_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
@@ -18657,6 +18453,6 @@ var ATelInput_default = /* @__PURE__ */ export_helper_default(_sfc_main, [
18657
18453
  ["__file", "/Users/alikhalill/Desktop/my-projects/ali-nuxt-toolkit/packages/ui-components/ATelInput/src/components/ATelInput.vue"]
18658
18454
  ]);
18659
18455
  //#endregion
18660
- export { ACountryFlag_default as ACountryFlag, ACountrySelect_default as ACountrySelect, ATelInput_default as ATelInput, COUNTRY_RECENTS_KEY, DEFAULT_ERROR_MESSAGES, DEFAULT_MESSAGES, DIAL_TO_ISO2_FALLBACK, LOCALE_DIGIT_RANGES, aTelInputVariants, defaultFlagUrl, detectCountry, localizeCountries, normalizeDigits, resolveMessages, useCountryDetection, useCountryMatching, usePhoneValidation, useTelInputValidation, useTypingPhase };
18456
+ export { ACountryFlag_default as ACountryFlag, ACountrySelect_default as ACountrySelect, ATelInput_default as ATelInput, COUNTRY_RECENTS_KEY, DEFAULT_ERROR_MESSAGES, DEFAULT_MESSAGES, DIAL_TO_ISO2_FALLBACK, FALLBACK_ISO2_LIST, LOCALE_DIGIT_RANGES, aTelInputVariants, defaultFlagUrl, detectCountry, localizeCountries, normalizeDigits, resolveMessages, useCountryDetection, useCountryMatching, useCountrySelection, usePhoneValidation, useSyncedModel, useTelInputValidation, useTypingPhase };
18661
18457
 
18662
18458
  //# sourceMappingURL=index.js.map