svelte-tel-input 4.0.0 → 4.0.2

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.
@@ -1,9 +1,11 @@
1
1
  <script lang="ts">
2
2
  import { onMount, untrack } from 'svelte';
3
3
  import { ParseError } from 'libphonenumber-js/max';
4
- import { generatePlaceholder, parsePhoneInput, telInputAction } from '../../utils/index.js';
4
+ import { generatePlaceholder } from '../../utils/index.js';
5
+ import { parsePhoneInput } from '../../utils/helpers.js';
6
+ import { telInputAction } from '../../utils/directives/telInputAction.js';
7
+ import { getCountry, guessCountryByPartialNumber } from '../../utils/countryHelpers.js';
5
8
  import type { CountryCode, TelInputOptions, Props, ValidationError } from '../../types';
6
- import { getCountry, guessCountryByPartialNumber } from '../../utils/directives/countryHelpers';
7
9
 
8
10
  const defaultOptions = {
9
11
  autoPlaceholder: true,
@@ -31,7 +33,7 @@
31
33
  defaultCountry = null,
32
34
  detailedValue = $bindable(null),
33
35
  valid = $bindable(true),
34
- validationError = $bindable<ValidationError>(null),
36
+ validationError = $bindable<Readonly<ValidationError>>(null),
35
37
  options = defaultOptions,
36
38
  el = $bindable(undefined),
37
39
  'aria-invalid': ariaInvalidProp = undefined,
@@ -161,8 +163,18 @@
161
163
  const getValidationError = (
162
164
  isEmpty: boolean,
163
165
  parseValid: boolean,
164
- resolvedCountry: CountryCode | null | undefined
166
+ resolvedCountry: CountryCode | null | undefined,
167
+ currentCountry: CountryCode | null | undefined = country
165
168
  ): ValidationError => {
169
+ if (
170
+ combinedOptions.lockCountry &&
171
+ currentCountry != null &&
172
+ resolvedCountry != null &&
173
+ resolvedCountry !== currentCountry
174
+ ) {
175
+ return 'COUNTRY_NOT_ALLOWED';
176
+ }
177
+
166
178
  const allowed = combinedOptions.allowedCountries;
167
179
  if (allowed?.length && resolvedCountry != null && !allowed.includes(resolvedCountry)) {
168
180
  return 'COUNTRY_NOT_ALLOWED';
@@ -176,9 +188,10 @@
176
188
  const applyValidity = (
177
189
  isEmpty: boolean,
178
190
  parseValid: boolean,
179
- resolvedCountry?: CountryCode | null
191
+ resolvedCountry?: CountryCode | null,
192
+ currentCountry: CountryCode | null | undefined = country
180
193
  ) => {
181
- const error = getValidationError(isEmpty, parseValid, resolvedCountry);
194
+ const error = getValidationError(isEmpty, parseValid, resolvedCountry, currentCountry);
182
195
  valid = error === null;
183
196
  validationError = error;
184
197
  onValidityChange?.(valid, error);
@@ -193,7 +206,12 @@
193
206
  if (inputValue === '') {
194
207
  applyValidity(true, false, country);
195
208
  } else {
196
- applyValidity(false, detailedValue?.isValid ?? false, country);
209
+ applyValidity(
210
+ false,
211
+ detailedValue?.isPhoneValid ?? false,
212
+ detailedValue?.countryCode ?? country,
213
+ country
214
+ );
197
215
  }
198
216
  }
199
217
  const { onblur } = rest;
@@ -319,21 +337,22 @@
319
337
  }
320
338
  }
321
339
 
322
- // If the resolved country is not in allowedCountries, the phone number is
323
- // intrinsically valid (libphonenumber-js says so) but the application rejects
324
- // it. Patch detailedValue to reflect this so that detailedValue.isValid is
325
- // consistent with the component's `valid` prop and `validationError`.
326
- const _allowedCountries = combinedOptions.allowedCountries;
327
- if (
328
- detailedValue &&
329
- _allowedCountries?.length &&
330
- detailedValue.countryCode != null &&
331
- !_allowedCountries.includes(detailedValue.countryCode)
332
- ) {
340
+ const resolvedCountry =
341
+ detailedValue?.countryCode ?? numberHasCountry?.iso2 ?? country ?? null;
342
+ const componentValidationError = detailedValue
343
+ ? getValidationError(
344
+ false,
345
+ detailedValue.isPhoneValid ?? false,
346
+ resolvedCountry,
347
+ currCountry ?? country
348
+ )
349
+ : null;
350
+
351
+ if (detailedValue && componentValidationError !== detailedValue.validationError) {
333
352
  detailedValue = {
334
353
  ...detailedValue,
335
- isValid: false,
336
- validationError: 'COUNTRY_NOT_ALLOWED'
354
+ isValid: componentValidationError === null && detailedValue.isPhoneValid,
355
+ validationError: componentValidationError
337
356
  };
338
357
  }
339
358
 
@@ -348,7 +367,12 @@
348
367
  onValueChange?.(value, detailedValue);
349
368
 
350
369
  if (shouldValidate) {
351
- applyValidity(false, detailedValue?.isValid ?? false, country);
370
+ applyValidity(
371
+ false,
372
+ detailedValue?.isPhoneValid ?? false,
373
+ resolvedCountry,
374
+ currCountry ?? country
375
+ );
352
376
  }
353
377
  };
354
378
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export { default as TelInput } from './components/input/TelInput.svelte';
2
- export { inputParser, clickOutsideAction, isSelected, parsePhoneInput, parse, normalizeToE164, pickCountries } from './utils/index.js';
3
- export { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js/max';
2
+ export { parse, normalizeToE164, pickCountries } from './utils/index.js';
4
3
  export { countries } from './assets/index.js';
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
1
  export { default as TelInput } from './components/input/TelInput.svelte';
2
- export { inputParser, clickOutsideAction, isSelected, parsePhoneInput, parse, normalizeToE164, pickCountries } from './utils/index.js';
3
- export { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js/max';
2
+ export { parse, normalizeToE164, pickCountries } from './utils/index.js';
4
3
  export { countries } from './assets/index.js';
@@ -1,4 +1,4 @@
1
- import type { CountryCallingCode, CountryCode, MetadataJson, PhoneNumber } from 'libphonenumber-js';
1
+ import type { CountryCode } from 'libphonenumber-js';
2
2
  import type { HTMLInputAttributes } from 'svelte/elements';
3
3
 
4
4
  export interface Country {
@@ -38,13 +38,13 @@ export interface DetailedValue {
38
38
  uri: string | null;
39
39
  e164: string | null;
40
40
  /** Granular validation error when `isValid` is `false`. */
41
- validationError?: ValidationError;
41
+ validationError: ValidationError;
42
42
  }
43
43
 
44
44
  /**
45
45
  * The reason the current phone number input is invalid.
46
46
  * - `'REQUIRED'` — field is empty and `required` is `true`
47
- * - `'COUNTRY_NOT_ALLOWED'` — the resolved country is not in `options.allowedCountries`
47
+ * - `'COUNTRY_NOT_ALLOWED'` — the resolved country is not in `options.allowedCountries`, or `options.lockCountry` rejects an international number that resolves to a different country than the locked one
48
48
  * - `'TOO_SHORT'` — number has too few digits
49
49
  * - `'TOO_LONG'` — number has too many digits
50
50
  * - `'NOT_A_NUMBER'` — input does not look like a phone number at all
@@ -133,7 +133,7 @@ export interface Props extends HTMLInputAttributes {
133
133
  /** Validity of the input based on the config settings. */
134
134
  valid?: boolean;
135
135
  /** The reason the current value is invalid, or `null` when valid. */
136
- validationError?: ValidationError;
136
+ validationError?: Readonly<ValidationError>;
137
137
  /** You can turn on and off certain features by this object */
138
138
  options?: TelInputOptions;
139
139
  /** Binding to the underlying `<input>` element */
@@ -148,7 +148,7 @@ export interface Props extends HTMLInputAttributes {
148
148
  * @param newValidity The new validity state.
149
149
  * @param error The validation error type.
150
150
  */
151
- onValidityChange?: (newValidity: boolean, error: ValidationError) => void;
151
+ onValidityChange?: (newValidity: boolean, error: Readonly<ValidationError>) => void;
152
152
  /**
153
153
  * Callback fired when the value or details change.
154
154
  * @param newValue The new E164 value.
@@ -166,4 +166,4 @@ export interface Props extends HTMLInputAttributes {
166
166
  onLoad?: () => void;
167
167
  }
168
168
 
169
- export type { CountryCallingCode, CountryCode, PhoneNumber, MetadataJson };
169
+ export type { CountryCode };
@@ -1,4 +1,4 @@
1
- import type { CountryCode, Country } from '../../types/index.js';
1
+ import type { CountryCode, Country } from '../types/index.js';
2
2
  export declare const getCountry: ({ field, value, countries }: {
3
3
  /**
4
4
  * Field to search by
@@ -1,4 +1,4 @@
1
- import { countries as normalizedCountries } from '../../assets/index.js';
1
+ import { countries as normalizedCountries } from '../assets/index.js';
2
2
  export const getCountry = ({ field, value, countries = normalizedCountries }) => {
3
3
  if (['priority'].includes(field)) {
4
4
  throw new Error(`Field "${field}" is not supported`);
@@ -1,6 +1,6 @@
1
- import { getCountry } from './countryHelpers.js';
1
+ import { getCountry } from '../countryHelpers.js';
2
2
  import { parsePhoneInput } from '../helpers.js';
3
- import { calculateCursorPosition } from './cursorPosition.js';
3
+ import { calculateCursorPosition } from '../cursorPosition.js';
4
4
  let inputState = null;
5
5
  const normalizeUserInput = (input) => {
6
6
  let value = '';
@@ -1,6 +1,6 @@
1
1
  import { AsYouType, getExampleNumber, formatIncompletePhoneNumber, validatePhoneNumberLength } from 'libphonenumber-js/max';
2
2
  import { examplePhoneNumbers, countries } from '../assets/index.js';
3
- import { getCountry } from './directives/countryHelpers.js';
3
+ import { getCountry } from './countryHelpers.js';
4
4
  const whiteSpaceRegex = new RegExp('[\\t\\n\\v\\f\\r \\u00a0\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u2028\\u2029\\u3000]', 'g');
5
5
  const plusSignRegex = new RegExp('\\+', 'g');
6
6
  export const generatePlaceholder = (country, { spaces } = {
@@ -1,3 +1 @@
1
- export { generatePlaceholder, inputParser, isSelected, parse, normalizeToE164, parsePhoneInput, pickCountries } from './helpers.js';
2
- export * from './directives/clickOutsideAction.js';
3
- export * from './directives/telInputAction.js';
1
+ export { generatePlaceholder, parse, normalizeToE164, pickCountries } from './helpers.js';
@@ -1,3 +1 @@
1
- export { generatePlaceholder, inputParser, isSelected, parse, normalizeToE164, parsePhoneInput, pickCountries } from './helpers.js';
2
- export * from './directives/clickOutsideAction.js';
3
- export * from './directives/telInputAction.js';
1
+ export { generatePlaceholder, parse, normalizeToE164, pickCountries } from './helpers.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "svelte-tel-input",
3
3
  "description": "svelte-tel-input",
4
- "version": "4.0.0",
4
+ "version": "4.0.2",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/gyurielf/svelte-tel-input.git"
@@ -47,7 +47,7 @@
47
47
  "micromatch": "^4.0.8",
48
48
  "postcss": "^8.5.8",
49
49
  "publint": "^0.3.18",
50
- "svelte": "^5.54.0",
50
+ "svelte": "^5.55.0",
51
51
  "svelte-check": "^4.4.5",
52
52
  "svelte2tsx": "^0.7.52",
53
53
  "tailwindcss": "^4.2.2",
@@ -55,7 +55,7 @@
55
55
  "typescript": "^5.9.3",
56
56
  "valibot": "^1.3.1",
57
57
  "vite": "^7.3.1",
58
- "vitest": "^4.1.0",
58
+ "vitest": "^4.1.1",
59
59
  "zod": "^4.3.6"
60
60
  },
61
61
  "type": "module",
@@ -1,3 +0,0 @@
1
- export declare const clickOutsideAction: (node: HTMLElement, handler: () => void, skipPrevented?: boolean) => {
2
- destroy: () => void;
3
- };
@@ -1,18 +0,0 @@
1
- export const clickOutsideAction = (node, handler, skipPrevented = true) => {
2
- const handleClick = async (event) => {
3
- if (skipPrevented) {
4
- if (!node.contains(event.target) && !event.defaultPrevented)
5
- handler();
6
- }
7
- else {
8
- if (!node.contains(event.target))
9
- handler();
10
- }
11
- };
12
- document.addEventListener('click', handleClick, true);
13
- return {
14
- destroy() {
15
- document.removeEventListener('click', handleClick, true);
16
- }
17
- };
18
- };