@alikhalilll/a-tel-input 1.0.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 (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/dist/index.cjs +5846 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +791 -0
  6. package/dist/index.d.ts +791 -0
  7. package/dist/index.js +5804 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/nuxt/index.cjs +30 -0
  10. package/dist/nuxt/index.cjs.map +1 -0
  11. package/dist/nuxt/index.d.cts +15 -0
  12. package/dist/nuxt/index.d.ts +15 -0
  13. package/dist/nuxt/index.js +30 -0
  14. package/dist/nuxt/index.js.map +1 -0
  15. package/dist/resolver/index.cjs +25 -0
  16. package/dist/resolver/index.cjs.map +1 -0
  17. package/dist/resolver/index.d.cts +14 -0
  18. package/dist/resolver/index.d.ts +14 -0
  19. package/dist/resolver/index.js +25 -0
  20. package/dist/resolver/index.js.map +1 -0
  21. package/dist/styles.css +520 -0
  22. package/package.json +123 -0
  23. package/src/components/ACountryFlag.vue +78 -0
  24. package/src/components/ACountrySelect.vue +674 -0
  25. package/src/components/ATelInput.vue +742 -0
  26. package/src/composables/useCountryDetection.ts +247 -0
  27. package/src/composables/useCountryMatching.ts +213 -0
  28. package/src/composables/usePhoneValidation.ts +573 -0
  29. package/src/composables/useTelInputValidation.ts +136 -0
  30. package/src/composables/useTypingPhase.ts +88 -0
  31. package/src/icons/AlertCircleIcon.vue +17 -0
  32. package/src/icons/CheckCircleIcon.vue +16 -0
  33. package/src/icons/CheckIcon.vue +15 -0
  34. package/src/icons/ChevronDownIcon.vue +15 -0
  35. package/src/icons/SearchIcon.vue +16 -0
  36. package/src/icons/SpinnerIcon.vue +28 -0
  37. package/src/icons/index.ts +6 -0
  38. package/src/index.ts +36 -0
  39. package/src/nuxt/index.ts +37 -0
  40. package/src/resolver/index.ts +29 -0
  41. package/src/types.ts +389 -0
  42. package/src/utils/digits.ts +42 -0
  43. package/src/utils/flag-url.ts +10 -0
  44. package/web-types.json +526 -0
@@ -0,0 +1,791 @@
1
+ import { ComputedRef, HTMLAttributes, Ref } from "vue";
2
+ import { VariantProps } from "class-variance-authority";
3
+
4
+ //#region src/composables/useCountryDetection.d.ts
5
+ type DetectionStrategy = 'auto' | 'locale' | 'none';
6
+ interface DetectCountryOptions {
7
+ /**
8
+ * - `'auto'` — try IP geolocation first, then timezone, then `navigator.language`, then default.
9
+ * - `'locale'` — skip the network call, use timezone + locale only.
10
+ * - `'none'` — return `defaultCountry` immediately.
11
+ */
12
+ strategy?: DetectionStrategy;
13
+ /** Endpoint returning a JSON body with a `country_code` (or `country`) field. */
14
+ ipEndpoint?: string;
15
+ /** Fallback ISO2 used when every step fails. */
16
+ defaultCountry?: string;
17
+ /** Abort the IP request after this many ms. */
18
+ timeoutMs?: number;
19
+ /** Persist the resolved country in sessionStorage so re-mounts within the session skip detection. */
20
+ cache?: boolean;
21
+ }
22
+ /**
23
+ * Imperative API. Use this when you want to call detection from outside Vue (e.g. inside a
24
+ * non-component composable, server middleware, or unit test).
25
+ */
26
+ declare function detectCountry(opts?: DetectCountryOptions): Promise<string>;
27
+ interface UseCountryDetectionReturn {
28
+ /** Resolved ISO2 code. Initially `null` until detection completes. */
29
+ country: Ref<string | null>;
30
+ /** True while detection is in flight. */
31
+ isLoading: Ref<boolean>;
32
+ /** Manually re-run detection (e.g. after the user changes their VPN). */
33
+ refresh: () => Promise<string>;
34
+ }
35
+ /**
36
+ * Reactive wrapper. Kicks off detection in `onMounted` so SSR renders an empty value and the
37
+ * client patches in the real country once resolved.
38
+ */
39
+ declare function useCountryDetection(opts?: DetectCountryOptions): UseCountryDetectionReturn;
40
+ //#endregion
41
+ //#region src/composables/usePhoneValidation.d.ts
42
+ interface RestCountry {
43
+ name?: {
44
+ common?: string;
45
+ };
46
+ cca2?: string;
47
+ idd?: {
48
+ root?: string;
49
+ suffixes?: string[];
50
+ };
51
+ flags?: {
52
+ png?: string;
53
+ svg?: string;
54
+ };
55
+ }
56
+ interface CountryOption<T = RestCountry> {
57
+ /** Display label, e.g. "Egypt (+20)". */
58
+ label: string;
59
+ /** Stable unique ID — the ISO 3166-1 alpha-2 code, e.g. "EG". */
60
+ value: string;
61
+ /** Precomputed normalized string for fast substring search. */
62
+ search_key: string;
63
+ raw_data: {
64
+ iso2: string;
65
+ dial_code: string;
66
+ dial_digits: string;
67
+ name: string;
68
+ flag: string | null;
69
+ source: 'restcountries' | 'fallback';
70
+ original: T;
71
+ };
72
+ }
73
+ interface PhoneRequiredInfo {
74
+ iso2: string;
75
+ dial_code: string;
76
+ /** Empty by default — consumer passes a placeholder via the component prop. */
77
+ placeholder: string;
78
+ example_national: string;
79
+ example_e164: string;
80
+ national_number_length: {
81
+ min: number | null;
82
+ max: number | null;
83
+ };
84
+ format_hint: string;
85
+ }
86
+ type PhoneValidationReason = 'missing_country' | 'country_not_supported' | 'phone_has_non_digits' | 'too_short' | 'too_long' | 'invalid_phone' | 'parse_failed';
87
+ interface PhoneValidationResult {
88
+ ok: boolean;
89
+ reason: PhoneValidationReason | null;
90
+ country: {
91
+ iso2: string;
92
+ dial_code: string;
93
+ } | null;
94
+ phone: {
95
+ raw: string | null;
96
+ digits: string;
97
+ };
98
+ full_phone: string | null;
99
+ required: PhoneRequiredInfo | null;
100
+ details?: Record<string, unknown>;
101
+ }
102
+ type ValidateArgs = {
103
+ country: {
104
+ iso2: string;
105
+ dial_code?: string;
106
+ } | null | undefined;
107
+ phone?: undefined; /** BCP-47 locale — localizes the numerals in the returned `required.format_hint`. */
108
+ locale?: string;
109
+ } | {
110
+ country: {
111
+ iso2: string;
112
+ dial_code?: string;
113
+ } | null | undefined;
114
+ phone: string | null; /** BCP-47 locale — localizes the numerals in the returned `required.format_hint`. */
115
+ locale?: string;
116
+ };
117
+ /**
118
+ * Return a copy of the country list with display names localized to `locale` via
119
+ * `Intl.DisplayNames`. `search_key` is rebuilt (keeping the English name too) so search
120
+ * still matches either spelling. Unknown locales / regions fall back to the English name.
121
+ */
122
+ declare function localizeCountries(list: CountryOption[], locale?: string): CountryOption[];
123
+ interface UsePhoneValidationReturn {
124
+ countries: Ref<CountryOption[]>;
125
+ isCountriesLoading: Ref<boolean>;
126
+ getCountries(options?: {
127
+ force?: boolean;
128
+ }): Promise<CountryOption[]>;
129
+ searchCountries(keyword: string, limit?: number): CountryOption[];
130
+ getCountryByValue(value: string): CountryOption | null;
131
+ getCountriesByDial(dial: string): CountryOption[];
132
+ getRequiredInfo(country: {
133
+ iso2: string;
134
+ dial_code?: string;
135
+ }, locale?: string): PhoneRequiredInfo | null;
136
+ validate(input: ValidateArgs): PhoneValidationResult;
137
+ }
138
+ declare function usePhoneValidation(): UsePhoneValidationReturn;
139
+ //#endregion
140
+ //#region src/utils/flag-url.d.ts
141
+ /**
142
+ * Default flag URL builder — flagcdn.com hosts PNG flags at multiple widths and is
143
+ * generous with caching + no API key required. Swap via the `flagUrl` prop on
144
+ * ATelInput / ACountrySelect / ACountryFlag to use any other source.
145
+ */
146
+ declare function defaultFlagUrl(iso2: string, width?: number): string;
147
+ type FlagUrlBuilder = (iso2: string, width: number) => string;
148
+ //#endregion
149
+ //#region ../AUiBase/dist/index.d.ts
150
+ //#endregion
151
+ //#region src/sizes.d.ts
152
+ /**
153
+ * Shared size scale for every interactive component in the @alikhalilll/a-* set.
154
+ *
155
+ * xs = 28px · sm = 36px · md = 43px (default) · lg = 52px · xl = 60px
156
+ *
157
+ * Use the {@link controlHeight}, {@link controlPaddingX}, {@link controlTextSize}
158
+ * maps when building a CVA variant so every component stays in lockstep.
159
+ */
160
+ type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
161
+ //#endregion
162
+ //#region src/types.d.ts
163
+ /** Alias for the shared `Size` scale — kept for backwards-friendly naming. */
164
+ type ATelInputSize = Size;
165
+ declare const aTelInputVariants: (props?: ({
166
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
167
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
168
+ type ATelInputVariants = VariantProps<typeof aTelInputVariants>;
169
+ /** Text direction for the field. `'auto'` (or omitting the prop) inherits from the
170
+ * nearest `[dir]` ancestor / `<html dir>`; `'ltr'` / `'rtl'` force it. */
171
+ type ATelInputDir = 'ltr' | 'rtl' | 'auto';
172
+ /**
173
+ * Every user-facing string in the tel-input UI, bundled so a consumer can localize the
174
+ * component in one prop. Each key has an English default in {@link DEFAULT_MESSAGES}.
175
+ */
176
+ interface TelInputMessages {
177
+ /** Placeholder of the country-picker search box. */
178
+ searchPlaceholder: string;
179
+ /** Shown when a search yields no countries. */
180
+ emptyText: string;
181
+ /** Shown while the country list is loading. */
182
+ loadingText: string;
183
+ /** Header of the "Suggested" group (current + recent picks). */
184
+ suggestedLabel: string;
185
+ /** Header of the full country list. */
186
+ allCountriesLabel: string;
187
+ /** Validation error text, keyed by reason. */
188
+ errorMessages: Record<PhoneValidationReason, string>;
189
+ /** Prefix of the country trigger's `aria-label`, e.g. `"Country: Egypt"`. */
190
+ countryLabel: string;
191
+ /** `aria-label` of the country trigger when no country is selected. */
192
+ selectCountryLabel: string;
193
+ /** `aria-label` of the phone input element. */
194
+ phoneInputLabel: string;
195
+ }
196
+ /** Partial override shape for the `messages` prop — every key (and every error reason) is optional. */
197
+ type TelInputMessagesInput = Partial<Omit<TelInputMessages, 'errorMessages'>> & {
198
+ errorMessages?: Partial<Record<PhoneValidationReason, string>>;
199
+ };
200
+ interface ATelInputProps {
201
+ class?: HTMLAttributes['class'];
202
+ placeholder?: string;
203
+ disabled?: boolean;
204
+ loading?: boolean;
205
+ size?: ATelInputSize;
206
+ /**
207
+ * Text direction. Omit (or pass `'auto'`) to inherit from the page — RTL pages get an
208
+ * RTL field automatically. Pass `'ltr'` / `'rtl'` to force it.
209
+ */
210
+ dir?: ATelInputDir;
211
+ /**
212
+ * BCP-47 locale (e.g. `'ar'`, `'fr'`). When set, country names render localized via
213
+ * `Intl.DisplayNames` and the format hint uses the locale's numerals.
214
+ */
215
+ locale?: string;
216
+ /**
217
+ * Localized UI strings. A single bag covering the picker, validation errors, and a11y
218
+ * labels. Individual props (`searchPlaceholder`, `emptyText`, `loadingText`,
219
+ * `errorMessages`) take precedence over the matching `messages` key when both are set.
220
+ */
221
+ messages?: TelInputMessagesInput;
222
+ /**
223
+ * Whitelist of allowed dial-digit codes (no `+`), e.g. `['20', '966']`.
224
+ * Countries outside this list are still shown in the picker but rendered as disabled.
225
+ */
226
+ allowedDialCodes?: string[];
227
+ /** Light up the field's validation styling — coloured border + ring on the input and the
228
+ * error message line below — when the number is valid / invalid. Default `false`, so the
229
+ * field stays neutral and validation surfacing is left to the consumer (via the
230
+ * `validation` ref exposure). */
231
+ showValidation?: boolean;
232
+ /** Show the green check / red alert icon at the end of the field. Default `false`; opt
233
+ * in with `true`. Independent of `showValidation` — you can show the icon without the
234
+ * coloured field, or vice versa. The slots `#valid-icon` / `#error-icon` still apply. */
235
+ showValidationIcon?: boolean;
236
+ /**
237
+ * Country auto-detect strategy. Defaults to `'auto'` — try IP geolocation first, then
238
+ * timezone, then `navigator.language`, finally `defaultCountry`.
239
+ */
240
+ detectCountry?: DetectionStrategy;
241
+ /**
242
+ * Initial country. Accepts either an ISO2 code (`'EG'`) or a dial-digit string
243
+ * (`'20'`, `'+20'`). When set, the picker is visible at mount with this country
244
+ * pre-selected — overrides the hidden-until-detected default.
245
+ */
246
+ defaultCountry?: string;
247
+ /** Override the IP geolocation endpoint. Must return JSON with `country_code` or `country`. */
248
+ ipEndpoint?: string;
249
+ /** Localized strings for the country picker UI. */
250
+ searchPlaceholder?: string;
251
+ emptyText?: string;
252
+ loadingText?: string;
253
+ /** Error labels keyed by reason. Each gets a sensible English default. */
254
+ errorMessages?: Partial<Record<PhoneValidationReason, string>>;
255
+ /**
256
+ * When true, the country picker is hidden until a leading dial code is detected in the
257
+ * phone input. Every keystroke runs a longest-prefix match against known dial codes; on
258
+ * first match the picker reveals with that country and the matched dial digits are
259
+ * stripped from `phone`. Skips the onMount IP/timezone/locale detection chain.
260
+ */
261
+ detectFromInput?: boolean;
262
+ /**
263
+ * Debounce window (ms) for `detectFromInput` detection. Each keystroke schedules the
264
+ * libphonenumber parse + lookup; bursts of typing/paste collapse into a single call.
265
+ * Clearing the input is not debounced — the picker hides immediately. Default 150ms.
266
+ */
267
+ detectDebounceMs?: number;
268
+ /** Override the flag URL builder, forwarded to ACountrySelect. */
269
+ flagUrl?: (iso2: string, width: number) => string;
270
+ /** Custom search predicate, forwarded to ACountrySelect. */
271
+ searcher?: (query: string, country: CountryOption) => boolean;
272
+ /** Provide your own country list, forwarded to ACountrySelect. */
273
+ countries?: CountryOption[];
274
+ /**
275
+ * Fully custom country detection. When provided, this function runs in place of the
276
+ * built-in chain — `detectCountry`-style options are still honored but the function
277
+ * receives them and is free to ignore them.
278
+ */
279
+ detector?: (options: DetectCountryOptions) => Promise<string | null | undefined>;
280
+ /** Forwarded to ACountrySelect: classes for the popover content surface. */
281
+ contentClass?: string;
282
+ /** Forwarded to ACountrySelect: classes for the desktop popover surface. */
283
+ popoverClass?: string;
284
+ /** Forwarded to ACountrySelect: classes for the mobile drawer surface. */
285
+ drawerClass?: string;
286
+ /** Classes for the inner phone field input element. */
287
+ inputClass?: string;
288
+ /** Classes for the outer wrapper that holds country select + input. */
289
+ fieldClass?: string;
290
+ /** Classes for the helper hint line. */
291
+ hintClass?: string;
292
+ /** Classes for the error message line. */
293
+ errorClass?: string;
294
+ /**
295
+ * How page scroll is blocked while the country popover is open. Defaults to `'events'`
296
+ * (sticky-safe document-level lock). Pass `'body'` for the legacy
297
+ * `body { overflow: hidden }` lock, or `'none'` to leave page scrolling alone.
298
+ */
299
+ scrollLock?: 'events' | 'body' | 'none';
300
+ }
301
+ /**
302
+ * Props for {@link ACountryFlag} — the standalone flag image component. Renders a
303
+ * `flagcdn` image for an ISO2 code with an automatic text-badge fallback when the
304
+ * image fails to load. Surface separately so it can be used outside `ATelInput`
305
+ * (e.g., in a custom country picker).
306
+ */
307
+ interface ACountryFlagProps {
308
+ /** ISO 3166-1 alpha-2 country code, case-insensitive. */
309
+ iso2: string;
310
+ /** Pixel width served by flagcdn. 40 is crisp at retina up to ~24px wide. */
311
+ width?: number;
312
+ /** Optional explicit URL override. When set, `iso2` / `width` / `flagUrl` are ignored. */
313
+ src?: string | null;
314
+ /** Function `(iso2, width) => string` — fully replace the URL builder. */
315
+ flagUrl?: FlagUrlBuilder;
316
+ alt?: string;
317
+ class?: HTMLAttributes['class'];
318
+ }
319
+ /**
320
+ * Slot prop shape for {@link ACountryFlag}. The `empty` slot is rendered when no
321
+ * flag URL is available and no ISO2 fallback can be derived.
322
+ */
323
+ interface ACountryFlagSlots {
324
+ /** Rendered when the flag URL is unavailable and no ISO2 text fallback can be derived. */
325
+ empty?: () => unknown;
326
+ }
327
+ declare const DEFAULT_ERROR_MESSAGES: Record<PhoneValidationReason, string>;
328
+ /** English defaults for every {@link TelInputMessages} key. */
329
+ declare const DEFAULT_MESSAGES: TelInputMessages;
330
+ /**
331
+ * Merge a partial `messages` override onto the English defaults. Used internally by
332
+ * `ATelInput` to resolve a complete {@link TelInputMessages} object.
333
+ */
334
+ declare function resolveMessages(input?: TelInputMessagesInput): TelInputMessages;
335
+ /**
336
+ * Slot prop shape for {@link ATelInput}. Use to get full slot-prop type inference
337
+ * when overriding slots in a consumer template:
338
+ *
339
+ * <ATelInput #suffix="{ validationState }">…</ATelInput>
340
+ * ↑ inferred as `'idle' | 'valid' | 'error'`
341
+ *
342
+ * Or in TypeScript code:
343
+ * type SuffixProps = Parameters<NonNullable<ATelInputSlots['suffix']>>[0];
344
+ */
345
+ interface ATelInputSlots {
346
+ /** Content before the country select trigger (e.g. an icon). */
347
+ prefix?: () => unknown;
348
+ /** Content between the input and the validation icons. */
349
+ suffix?: (props: {
350
+ validationState: 'idle' | 'valid' | 'error';
351
+ validation: PhoneValidationResult;
352
+ }) => unknown;
353
+ /** Replace the green check shown when the number validates. */
354
+ 'valid-icon'?: () => unknown;
355
+ /** Replace the warning icon shown when the number fails validation. */
356
+ 'error-icon'?: (props: {
357
+ reason: string;
358
+ }) => unknown;
359
+ /** Replace the dim helper line shown below the input when empty. */
360
+ hint?: (props: {
361
+ country: string;
362
+ formatHint: string;
363
+ example: string | null;
364
+ }) => unknown;
365
+ /** Replace the error message rendered when invalid. */
366
+ error?: (props: {
367
+ message: string;
368
+ reason: string;
369
+ validation: PhoneValidationResult;
370
+ }) => unknown;
371
+ /** Forwarded to ACountrySelect — replace the trigger button. */
372
+ trigger?: (props: {
373
+ selectedCountry: CountryOption | null;
374
+ open: boolean;
375
+ sizeClasses: string;
376
+ }) => unknown;
377
+ /** Forwarded to ACountrySelect — replace the chevron. */
378
+ chevron?: (props: {
379
+ open: boolean;
380
+ }) => unknown;
381
+ /** Forwarded — replace any flag rendering. */
382
+ flag?: (props: {
383
+ country: CountryOption;
384
+ context: 'trigger' | 'item';
385
+ }) => unknown;
386
+ /** Forwarded — replace each country list row. */
387
+ item?: (props: {
388
+ country: CountryOption;
389
+ selected: boolean;
390
+ disabled: boolean;
391
+ select: () => void;
392
+ }) => unknown;
393
+ /** Forwarded — section header. */
394
+ 'group-header'?: (props: {
395
+ label: string;
396
+ group: 'suggested' | 'all';
397
+ }) => unknown;
398
+ /** Forwarded — search bar. */
399
+ search?: (props: {
400
+ value: string;
401
+ setValue: (v: string) => void;
402
+ isSearching: boolean;
403
+ }) => unknown;
404
+ loading?: () => unknown;
405
+ empty?: (props: {
406
+ query: string;
407
+ }) => unknown;
408
+ /** Replace the spinner shown in the picker slot during the debounce window. */
409
+ detecting?: () => unknown;
410
+ }
411
+ /**
412
+ * Emit map for {@link ATelInput}. `update:phone` carries the digits-only string,
413
+ * `update:country` carries the dial-number (not ISO2). Surface for consumers who
414
+ * wire the events manually instead of via `v-model:phone` / `v-model:country`.
415
+ */
416
+ type ATelInputEmits = {
417
+ 'update:phone': [value: string];
418
+ 'update:country': [value: number | null];
419
+ };
420
+ /**
421
+ * Props for {@link ACountrySelect} — the standalone country picker. Surface
422
+ * separately so it can be used outside `ATelInput` with full type support.
423
+ */
424
+ interface ACountrySelectProps {
425
+ class?: HTMLAttributes['class'];
426
+ triggerClass?: HTMLAttributes['class'];
427
+ contentClass?: HTMLAttributes['class'];
428
+ popoverClass?: HTMLAttributes['class'];
429
+ drawerClass?: HTMLAttributes['class'];
430
+ searchPlaceholder?: string;
431
+ emptyText?: string;
432
+ loadingText?: string;
433
+ suggestedLabel?: string;
434
+ allCountriesLabel?: string;
435
+ /** ISO2 codes that are selectable. Others are listed but disabled. */
436
+ allowedDialCodes?: string[];
437
+ disabled?: boolean;
438
+ /** Drives the trigger button padding + text size. Matches ATelInput's `size`. */
439
+ size?: ATelInputSize;
440
+ /** Max items rendered under the "Suggested" header (current + recents, deduped). */
441
+ suggestedLimit?: number;
442
+ /** Cap the number of matching countries shown in search results. */
443
+ maxResults?: number;
444
+ /** Override the flag URL builder, e.g. `(iso, w) => `/flags/${iso}.svg``. */
445
+ flagUrl?: (iso2: string, width: number) => string;
446
+ /** Custom search predicate. Default: substring match on the precomputed `search_key`. */
447
+ searcher?: (query: string, country: CountryOption) => boolean;
448
+ /** Provide your own country list (bypasses the REST Countries fetch). */
449
+ countries?: CountryOption[];
450
+ /** Override the right-side kbd hints. Pass `null` to hide. */
451
+ kbdOpen?: string | null;
452
+ kbdClose?: string | null;
453
+ /** BCP-47 locale — country names render localized via `Intl.DisplayNames`. */
454
+ locale?: string;
455
+ /** Prefix of the trigger's `aria-label` when a country is selected, e.g. `"Country"`. */
456
+ countryLabel?: string;
457
+ /** Trigger's `aria-label` when no country is selected. */
458
+ selectCountryLabel?: string;
459
+ /** How page scroll is blocked while the popover is open. Default `'events'`. */
460
+ scrollLock?: 'events' | 'body' | 'none';
461
+ }
462
+ /**
463
+ * Slot prop shape for {@link ACountrySelect}. Forwarded versions of these slots
464
+ * also appear on {@link ATelInputSlots} (`trigger`, `chevron`, `flag`, `item`,
465
+ * etc.) — keep them in sync when changing one.
466
+ */
467
+ interface ACountrySelectSlots {
468
+ /** Replace the entire country picker trigger button. */
469
+ trigger?: (props: {
470
+ selectedCountry: CountryOption | null;
471
+ open: boolean;
472
+ sizeClasses: string;
473
+ }) => unknown;
474
+ /** Replace the chevron icon. */
475
+ chevron?: (props: {
476
+ open: boolean;
477
+ }) => unknown;
478
+ /** Replace just the flag rendered in the trigger and items. */
479
+ flag?: (props: {
480
+ country: CountryOption;
481
+ context: 'trigger' | 'item';
482
+ }) => unknown;
483
+ /** Replace the entire search bar (input + icon + kbd). */
484
+ search?: (props: {
485
+ value: string;
486
+ setValue: (v: string) => void;
487
+ isSearching: boolean;
488
+ }) => unknown;
489
+ /** Replace the search-bar leading icon. */
490
+ 'search-icon'?: () => unknown;
491
+ /** Replace the loading state. */
492
+ loading?: () => unknown;
493
+ /** Replace the empty/no-results state. */
494
+ empty?: (props: {
495
+ query: string;
496
+ }) => unknown;
497
+ /** Replace a section header. */
498
+ 'group-header'?: (props: {
499
+ label: string;
500
+ group: 'suggested' | 'all';
501
+ }) => unknown;
502
+ /** Replace each country list row. */
503
+ item?: (props: {
504
+ country: CountryOption;
505
+ selected: boolean;
506
+ disabled: boolean;
507
+ select: () => void;
508
+ }) => unknown;
509
+ /** Replace just the right-side check icon for the selected row. */
510
+ 'item-check'?: (props: {
511
+ country: CountryOption;
512
+ }) => unknown;
513
+ }
514
+ /**
515
+ * Emit map for {@link ACountrySelect}. The `selected` is `v-model:selected`,
516
+ * carrying the ISO2 code of the picked country.
517
+ */
518
+ type ACountrySelectEmits = {
519
+ 'update:selected': [value: string];
520
+ };
521
+ //#endregion
522
+ //#region src/components/ATelInput.vue.d.ts
523
+ type __VLS_Props$1 = ATelInputProps;
524
+ type __VLS_Slots$2 = ATelInputSlots;
525
+ type __VLS_ModelProps$1 = {
526
+ 'phone'?: string;
527
+ /** Public `v-model:country` — the **dial number** (e.g. `20` for Egypt, `44` for the UK,
528
+ * `1` for the NANP block). `null` means no country selected. Internally the component
529
+ * tracks a richer ISO2 code (`selectedIso2`) because dial codes alone can't disambiguate
530
+ * NANP (`+1` covers 25+ countries) — the picker still needs an exact country. */
531
+ 'country'?: number | null;
532
+ };
533
+ type __VLS_PublicProps$1 = __VLS_Props$1 & __VLS_ModelProps$1;
534
+ declare const __VLS_base$2: import("vue").DefineComponent<__VLS_PublicProps$1, {
535
+ validation: import("vue").ComputedRef<PhoneValidationResult>;
536
+ required: import("vue").ComputedRef<PhoneRequiredInfo | null>;
537
+ selectedDialCode: import("vue").ComputedRef<string | null>;
538
+ validationState: import("vue").ComputedRef<"idle" | "valid" | "error">;
539
+ visibleValidationState: import("vue").ComputedRef<"idle" | "valid" | "error">;
540
+ isDetecting: Readonly<import("vue").Ref<boolean, boolean>>;
541
+ hasFinishedTyping: Readonly<import("vue").Ref<boolean, boolean>>;
542
+ detectionAttempted: Readonly<import("vue").Ref<boolean, boolean>>;
543
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
544
+ "update:phone": (value: string) => any;
545
+ "update:country": (value: number | null) => any;
546
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps$1> & Readonly<{
547
+ "onUpdate:phone"?: ((value: string) => any) | undefined;
548
+ "onUpdate:country"?: ((value: number | null) => any) | undefined;
549
+ }>, {
550
+ placeholder: string;
551
+ size: ATelInputSize;
552
+ showValidationIcon: boolean;
553
+ detectCountry: DetectionStrategy;
554
+ defaultCountry: string;
555
+ ipEndpoint: string;
556
+ detectFromInput: boolean;
557
+ detectDebounceMs: number;
558
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
559
+ declare const __VLS_export$2: typeof __VLS_base$2;
560
+ declare const _default$2: typeof __VLS_export$2;
561
+ //#endregion
562
+ //#region src/components/ACountrySelect.vue.d.ts
563
+ type __VLS_Props = ACountrySelectProps;
564
+ type __VLS_Slots$1 = ACountrySelectSlots;
565
+ declare function selectCountry(option: CountryOption): void;
566
+ type __VLS_ModelProps = {
567
+ 'selected'?: string;
568
+ };
569
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
570
+ declare const __VLS_base$1: import("vue").DefineComponent<__VLS_PublicProps, {
571
+ open: import("vue").Ref<boolean, boolean>;
572
+ setOpen: (v: boolean) => boolean;
573
+ search: import("vue").Ref<string, string>;
574
+ setSearch: (v: string) => string;
575
+ selectedCountry: import("vue").ComputedRef<CountryOption<RestCountry> | null>;
576
+ selectCountry: typeof selectCountry;
577
+ countries: import("vue").ComputedRef<CountryOption<RestCountry>[]>;
578
+ recents: import("vue").Ref<string[], string[]>;
579
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
580
+ "update:selected": (value: string) => any;
581
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
582
+ "onUpdate:selected"?: ((value: string) => any) | undefined;
583
+ }>, {
584
+ size: ATelInputSize;
585
+ searchPlaceholder: string;
586
+ emptyText: string;
587
+ loadingText: string;
588
+ suggestedLabel: string;
589
+ allCountriesLabel: string;
590
+ countryLabel: string;
591
+ selectCountryLabel: string;
592
+ suggestedLimit: number;
593
+ maxResults: number;
594
+ kbdOpen: string | null;
595
+ kbdClose: string | null;
596
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
597
+ declare const __VLS_export$1: typeof __VLS_base$1;
598
+ declare const _default$1: typeof __VLS_export$1;
599
+ //#endregion
600
+ //#region src/components/ACountryFlag.vue.d.ts
601
+ type __VLS_Slots = ACountryFlagSlots;
602
+ declare const __VLS_base: import("vue").DefineComponent<ACountryFlagProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ACountryFlagProps> & Readonly<{}>, {
603
+ width: number;
604
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
605
+ declare const __VLS_export: typeof __VLS_base;
606
+ declare const _default: typeof __VLS_export;
607
+ //#endregion
608
+ //#region src/utils/digits.d.ts
609
+ /**
610
+ * Alternative-numeral support. Phone numbers are routinely entered with the digits of the
611
+ * user's own script — Arabic-Indic, Persian/Urdu, Devanagari, Bengali. `libphonenumber-js`
612
+ * and our own `\d` cleanup only understand ASCII `0-9`, so anything else silently becomes
613
+ * an empty number. `normalizeDigits` folds those scripts down to ASCII before validation.
614
+ */
615
+ /**
616
+ * Base code points of contiguous decimal-digit blocks. Each block runs `base`‥`base+9`
617
+ * for digit `0`‥`9`, so the ASCII digit is `codePoint - base`. Add a script by appending
618
+ * one entry here.
619
+ */
620
+ declare const LOCALE_DIGIT_RANGES: {
621
+ name: string;
622
+ base: number;
623
+ }[];
624
+ /**
625
+ * Replace any supported non-ASCII decimal digit with its ASCII equivalent. Every other
626
+ * character (spaces, `+`, separators, letters) is left untouched — callers still run their
627
+ * own `\D` cleanup afterwards.
628
+ */
629
+ declare function normalizeDigits(input: string): string;
630
+ //#endregion
631
+ //#region src/composables/useCountryMatching.d.ts
632
+ /** Synchronous dial-digit → ISO2 fallback for common countries, used when the async
633
+ * REST Countries fetch hasn't populated `getCountriesByDial`'s index yet at setup. */
634
+ declare const DIAL_TO_ISO2_FALLBACK: Record<string, string>;
635
+ /** localStorage key for the user's most recently picked countries. Used as a
636
+ * tie-breaker when multiple countries share a dial code (e.g. all NANP). */
637
+ declare const COUNTRY_RECENTS_KEY = "ali_ui_country_recents_v1";
638
+ interface DialMatch {
639
+ country: CountryOption;
640
+ /** The national significant number — what the phone input should hold, with both the
641
+ * dial code and the national prefix (e.g. Egyptian leading `0`) stripped. */
642
+ nationalNumber: string;
643
+ }
644
+ interface MatchLeadingDialCodeOptions {
645
+ /** ISO2 hint for libphonenumber's national-format parse (tier 2). Typically the
646
+ * IP/timezone/locale-resolved country. */
647
+ hintCountry?: string;
648
+ /** Currently selected ISO2 — preferred when a shared dial code yields multiple
649
+ * countries (tier 3 tie-break). */
650
+ currentIso2?: string;
651
+ }
652
+ interface CountryMatchingDeps {
653
+ /** Country lookup by ISO2 code — typically `usePhoneValidation().getCountryByValue`. */
654
+ getCountryByValue: (value: string) => CountryOption | null;
655
+ /** Country lookup by dial digits — typically `usePhoneValidation().getCountriesByDial`. */
656
+ getCountriesByDial: (dial: string) => CountryOption[];
657
+ }
658
+ /**
659
+ * Country-matching helpers used by the tel input — pure functions on top of the
660
+ * `usePhoneValidation` country index. Split out of `ATelInput.vue` so the component
661
+ * script stays focused on UI state and the matching logic is independently testable.
662
+ *
663
+ * **Important**: takes the validation lookups as dependencies rather than calling
664
+ * `usePhoneValidation()` itself. `usePhoneValidation` creates a fresh state on every
665
+ * call, so calling it here would produce a *second* empty country index that never gets
666
+ * populated by the caller's `getCountries()` — the matcher would see no countries and
667
+ * all tier-3 prefix lookups would fall through to the (much smaller) fallback table.
668
+ */
669
+ declare function useCountryMatching(deps: CountryMatchingDeps): {
670
+ resolveCountryIdentifier: (raw: string | undefined | null) => string;
671
+ dialNumberFor: (iso2: string) => number | null;
672
+ matchLeadingDialCode: (digits: string, options?: MatchLeadingDialCodeOptions) => DialMatch | null;
673
+ };
674
+ //#endregion
675
+ //#region src/composables/useTypingPhase.d.ts
676
+ /**
677
+ * Typing-phase state machine for the tel input.
678
+ *
679
+ * Owns the three reactive flags that drive the "is the user still typing?" UX:
680
+ *
681
+ * - `isDetecting` — true while the debounce window is in flight (user is mid-burst or
682
+ * has just paused). Drives the loading spinner in the picker slot.
683
+ * - `hasFinishedTyping` — false from the moment a key lands until the debounce settles.
684
+ * Gates validation visibility, so error/success states only appear once the user pauses.
685
+ * - `detectionAttempted` — flips true the first time the debounce fires on non-empty
686
+ * input. Used by the consumer to keep the country picker visible after a failed
687
+ * detection (so the user can pick manually instead of being stranded).
688
+ *
689
+ * Design notes:
690
+ *
691
+ * - The composable is pure state — it does not know about country detection, phone
692
+ * numbers, or libphonenumber. The consumer wires the `onSettle` callback to whatever
693
+ * "what to do when typing pauses" logic is appropriate (typically: try to detect a
694
+ * country from the current digits, mark a detection attempt, and apply the match).
695
+ *
696
+ * - `markDetectionAttempt()` is exposed separately so the caller controls *when* the
697
+ * "keep the picker visible" flag flips — not every settle triggers a real attempt
698
+ * (e.g. when input is empty or the user already picked a country manually).
699
+ *
700
+ * - Refs are exposed `readonly` so external code can't bypass the state machine; all
701
+ * transitions go through the exposed actions.
702
+ */
703
+ interface UseTypingPhaseOptions {
704
+ /** Debounce window in ms. Reactive so consumers can change `detectDebounceMs` at runtime. */
705
+ debounceMs: ComputedRef<number>;
706
+ /** Fired when the debounce timer settles. Runs regardless of input state — use this
707
+ * to clear loading UI, then perform any pause-triggered work (e.g. detection). */
708
+ onSettle?: () => void;
709
+ }
710
+ interface UseTypingPhaseReturn {
711
+ isDetecting: Readonly<Ref<boolean>>;
712
+ hasFinishedTyping: Readonly<Ref<boolean>>;
713
+ detectionAttempted: Readonly<Ref<boolean>>;
714
+ /** Call from the input handler on every keystroke that produces non-empty input. */
715
+ markTyping: () => void;
716
+ /** Flip `detectionAttempted` to true. Call from within the `onSettle` callback when
717
+ * a real detection attempt is about to run — so the picker stays visible after even
718
+ * a failed match. */
719
+ markDetectionAttempt: () => void;
720
+ /** Reset all three flags to defaults. Call when the input is cleared. */
721
+ reset: () => void;
722
+ }
723
+ declare function useTypingPhase(opts: UseTypingPhaseOptions): UseTypingPhaseReturn;
724
+ //#endregion
725
+ //#region src/composables/useTelInputValidation.d.ts
726
+ /**
727
+ * Validation surfacing facade for ATelInput.
728
+ *
729
+ * Wraps the raw `usePhoneValidation()` calls and produces the *view-layer* surface the
730
+ * component needs:
731
+ *
732
+ * - `validation` / `validationState` — the raw + simplified state of the current input.
733
+ * - `visibleValidationState` — `validationState` gated by the `hasFinishedTyping` flag
734
+ * from {@link useTypingPhase}, so error tints / icons / messages only appear once the
735
+ * user has paused. This is the value the template should bind to.
736
+ * - `errorMessage` — localised error string for the current `validation.reason`, or
737
+ * `null` when the input is empty or valid.
738
+ * - `showError` / `showHint` — boolean computed properties for conditional rendering
739
+ * in the template; both already respect `showValidation` and the typing-pause gate.
740
+ * - `selectedDialCode` — the human-readable dial prefix (`+20`, `+1`, …) for the
741
+ * selected country, used as an in-input prefix.
742
+ *
743
+ * Design notes:
744
+ *
745
+ * - The composable takes the `usePhoneValidation()` return value as a *dependency*
746
+ * rather than calling `usePhoneValidation()` itself. That function creates a fresh
747
+ * country index per invocation; calling it here would produce a second, empty index
748
+ * that never gets populated by the caller's `getCountries()` (the same bug pattern
749
+ * {@link useCountryMatching} avoids).
750
+ *
751
+ * - All inputs are `Ref` / `ComputedRef` so reactivity flows correctly. Method
752
+ * references on the validation singleton (`validate`, `getRequiredInfo`,
753
+ * `getCountryByValue`) are passed verbatim — their backing state is reactive.
754
+ */
755
+ interface UseTelInputValidationDeps {
756
+ validate: UsePhoneValidationReturn['validate'];
757
+ getRequiredInfo: UsePhoneValidationReturn['getRequiredInfo'];
758
+ getCountryByValue: UsePhoneValidationReturn['getCountryByValue'];
759
+ }
760
+ interface UseTelInputValidationInputs {
761
+ /** Digits-only national number model. */
762
+ phone: Ref<string>;
763
+ /** Currently selected ISO2 — empty string when no country chosen. */
764
+ selectedIso2: Ref<string>;
765
+ /** From {@link useTypingPhase} — gates visible state during the debounce window. */
766
+ hasFinishedTyping: Readonly<Ref<boolean>>;
767
+ /** Resolved i18n messages (merged defaults + consumer overrides). */
768
+ messages: ComputedRef<TelInputMessages>;
769
+ }
770
+ interface UseTelInputValidationConfig {
771
+ /** BCP-47 locale; affects `format_hint` numeral rendering. */
772
+ locale: () => string | undefined;
773
+ /** Light up field tinting + error message line. From props. */
774
+ showValidation: () => boolean | undefined;
775
+ /** Per-reason error string overrides. From props. */
776
+ errorMessages: () => Partial<Record<PhoneValidationReason, string>> | undefined;
777
+ }
778
+ interface UseTelInputValidationReturn {
779
+ validation: ComputedRef<PhoneValidationResult>;
780
+ required: ComputedRef<PhoneRequiredInfo | null>;
781
+ validationState: ComputedRef<'idle' | 'valid' | 'error'>;
782
+ visibleValidationState: ComputedRef<'idle' | 'valid' | 'error'>;
783
+ errorMessage: ComputedRef<string | null>;
784
+ showError: ComputedRef<boolean>;
785
+ showHint: ComputedRef<boolean>;
786
+ selectedDialCode: ComputedRef<string | null>;
787
+ }
788
+ declare function useTelInputValidation(deps: UseTelInputValidationDeps, inputs: UseTelInputValidationInputs, config: UseTelInputValidationConfig): UseTelInputValidationReturn;
789
+ //#endregion
790
+ export { _default as ACountryFlag, type ACountryFlagProps, type ACountryFlagSlots, _default$1 as ACountrySelect, type ACountrySelectEmits, type ACountrySelectProps, type ACountrySelectSlots, _default$2 as ATelInput, type ATelInputDir, type ATelInputEmits, type ATelInputProps, type ATelInputSize, type ATelInputSlots, type ATelInputVariants, COUNTRY_RECENTS_KEY, CountryMatchingDeps, CountryOption, DEFAULT_ERROR_MESSAGES, DEFAULT_MESSAGES, DIAL_TO_ISO2_FALLBACK, DetectCountryOptions, DetectionStrategy, DialMatch, type FlagUrlBuilder, LOCALE_DIGIT_RANGES, MatchLeadingDialCodeOptions, PhoneRequiredInfo, PhoneValidationReason, PhoneValidationResult, RestCountry, type TelInputMessages, type TelInputMessagesInput, UseCountryDetectionReturn, UsePhoneValidationReturn, UseTelInputValidationConfig, UseTelInputValidationDeps, UseTelInputValidationInputs, UseTelInputValidationReturn, UseTypingPhaseOptions, UseTypingPhaseReturn, ValidateArgs, aTelInputVariants, defaultFlagUrl, detectCountry, localizeCountries, normalizeDigits, resolveMessages, useCountryDetection, useCountryMatching, usePhoneValidation, useTelInputValidation, useTypingPhase };
791
+ //# sourceMappingURL=index.d.cts.map