Skip to content

Commit

Permalink
fix(international-phone-input): correct preserve country code
Browse files Browse the repository at this point in the history
  • Loading branch information
hextion committed Jan 15, 2025
1 parent 6ebc169 commit 491e379
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 41 deletions.
6 changes: 6 additions & 0 deletions .changeset/mighty-eggs-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@alfalab/core-components-shared': minor
'@alfalab/core-components-international-phone-input': patch
---

- Исправлено автозаполнение номера в Safari 18
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useMaskito } from '@maskito/react';
import type { InputAutocompleteProps } from '@alfalab/core-components-input-autocomplete';
import { AnyObject, BaseOption } from '@alfalab/core-components-select/shared';
import type { BaseSelectChangePayload } from '@alfalab/core-components-select/typings';
import { isNullable } from '@alfalab/core-components-shared';

import type { BaseInternationalPhoneInputProps, Country } from '../../types';
import {
Expand Down Expand Up @@ -58,6 +59,7 @@ export const BaseInternationalPhoneInput = forwardRef<
},
ref,
) => {
const lastCountryRef = useRef<Country | null>(null);
const countriesData = useMemo(() => initCountries(countries), [countries]);
const inputRef = useRef<HTMLInputElement>(null);
const inputWrapperRef = useRef<HTMLDivElement>(null);
Expand All @@ -80,7 +82,13 @@ export const BaseInternationalPhoneInput = forwardRef<
const preserveCountryCode = clearableCountryCodeFromProps === 'preserve';
const clearableCountryCode = preserveCountryCode || clearableCountryCodeFromProps;
const maskOptions = useMemo(
() => createMaskOptions(country, clearableCountryCode, preserveCountryCode),
() =>
createMaskOptions(
country,
clearableCountryCode,
preserveCountryCode,
lastCountryRef,
),
[country, clearableCountryCode, preserveCountryCode],
);

Expand Down Expand Up @@ -202,6 +210,14 @@ export const BaseInternationalPhoneInput = forwardRef<
...restProps.inputProps,
} as const;

useEffect(() => {
if (!preserveCountryCode || isNullable(country)) {
return;
}

lastCountryRef.current = country;
}, [country, preserveCountryCode]);

return Array.isArray(options) ? (
<InputAutocomplete
closeOnSelect={true}
Expand Down
13 changes: 3 additions & 10 deletions packages/international-phone-input/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,11 @@ import type { InputAutocompleteDesktopProps } from '@alfalab/core-components-inp
import type { InputAutocompleteMobileProps } from '@alfalab/core-components-input-autocomplete/mobile';
import { OptionShape } from '@alfalab/core-components-select/typings';

import { Country } from '../../types';

import type { SharedCountrySelectProps } from './components/country-select';

export type Country = {
name: string;
regions?: string[];
iso2: string;
countryCode: string;
dialCode: string;
format?: string;
priority: number;
mainCode?: boolean;
};
export { Country };

export type AreaItem = Country & {
isAreaCode: boolean;
Expand Down
22 changes: 16 additions & 6 deletions packages/international-phone-input/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RefObject } from 'react';
import type { MaskitoOptions } from '@maskito/core';

import type { InputAutocompleteDesktopProps } from '@alfalab/core-components-input-autocomplete/desktop';
Expand Down Expand Up @@ -50,8 +51,8 @@ export function initCountries(iso2s?: string[]) {

export function findCountry(
countries: Country[][],
value?: string,
iso2?: string,
value: string | undefined,
iso2: string | undefined,
country?: Country,
) {
if (country) return country;
Expand Down Expand Up @@ -162,18 +163,27 @@ export function createMaskOptions(
country: Country | undefined,
clearableCountryCode: boolean,
preserveCountryCode: boolean,
lastCountryRef: RefObject<Country | null>,
): MaskitoOptions {
const countryCode = country?.countryCode;
const prefix = countryCode ? getInitialValueFromCountry(countryCode) : '';
const prefixLen = !clearableCountryCode && prefix ? prefix.length : 0;
const mask = createPhoneMaskExpression(country, clearableCountryCode);

const preprocessors = [
maskUtils.insertionPhonePreprocessor(mask, countryCode, clearableCountryCode),
];

if (preserveCountryCode) {
const createMask = (lastCountry: Country) =>
createPhoneMaskExpression(lastCountry, clearableCountryCode);

preprocessors.push(maskUtils.preserveCountryCodePreprocessor(lastCountryRef, createMask));
}

return {
mask,
preprocessors: [
maskUtils.insertionPhonePreprocessor(mask, countryCode, clearableCountryCode),
maskUtils.preserveCountryCodePreprocessor(countryCode, preserveCountryCode),
],
preprocessors,
postprocessors: [maskUtils.prefixPostprocessor(prefixLen > 0 ? prefix : '')],
plugins: [
maskUtils.caretGuard((value, [from, to]) => [
Expand Down
2 changes: 1 addition & 1 deletion packages/international-phone-input/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"include": ["src", "../../typings"],
"include": ["src", "../../typings", "../types"],
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
Expand Down
57 changes: 34 additions & 23 deletions packages/shared/src/maskUtils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
/* eslint-disable no-plusplus, no-param-reassign */
import { RefObject } from 'react';
import type { MaskitoPlugin } from '@maskito/core';
import { MaskitoPostprocessor, MaskitoPreprocessor } from '@maskito/core';

import { fnUtils } from './fnUtils';
import { Country } from '../../types';

import { fnUtils, isNonNullable } from './fnUtils';

/**
* Запрещает каретке становиться за указанные границы
Expand Down Expand Up @@ -88,7 +91,12 @@ function prefixPostprocessor(prefix: string): MaskitoPostprocessor {
: (state) => state;
}

const countDigits = (value: string): number => value.replace(/\D/g, '').length;
const clearMask = (value: string) => value.replace(/\D/g, '');

const countDigits = (value: string): number => clearMask(value).length;

const getCompletePhoneLength = (mask: Array<string | RegExp>) =>
mask.filter((item) => `${item}` === `${/\d/}` || /[0-9]/.test(`${item}`)).length;

/**
* Препроцессор необходим для правильной вставки/автокомплита телефонного номера
Expand All @@ -98,9 +106,7 @@ function insertionPhonePreprocessor(
countryCode: string | undefined,
clearableCountryCode: boolean | undefined,
): MaskitoPreprocessor {
const completePhoneLength = mask.filter(
(item) => `${item}` === `${/\d/}` || /[0-9]/.test(`${item}`),
).length;
const completePhoneLength = getCompletePhoneLength(mask);

const trimCountryPrefix = (value: string): string => {
if (countryCode === '7') {
Expand Down Expand Up @@ -142,25 +148,30 @@ function insertionPhonePreprocessor(
* Препроцессор необходим для сохранения кода страны при автозаполнении
*/
function preserveCountryCodePreprocessor(
countryCode: string | undefined,
preserveCountryCode: boolean,
countryRef: RefObject<Country | null>,
createMask: (country: Country) => Array<string | RegExp>,
): MaskitoPreprocessor {
return ({ elementState, data }) => {
if (!preserveCountryCode || fnUtils.isNil(countryCode)) {
return { elementState, data };
}

const { value, selection } = elementState;

if (value.length > 0 && !value.startsWith('+')) {
const nextValue = countryCode.concat(value);

return {
elementState: {
value: nextValue,
selection,
},
};
return ({ elementState }) => {
const country = countryRef.current;

if (isNonNullable(country)) {
const { value, selection } = elementState;
const { countryCode } = country;
const numbersValue = clearMask(value);

if (!numbersValue.startsWith(countryCode)) {
const nextValue = countryCode.concat(numbersValue);
const mask = createMask(country);

if (countDigits(nextValue) === getCompletePhoneLength(mask)) {
return {
elementState: {
selection,
value: nextValue,
},
};
}
}
}

return { elementState };
Expand Down
11 changes: 11 additions & 0 deletions packages/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,14 @@ export type TypographyType =
| 'accent-component'
| 'action-component'
| 'paragraph-component';

export type Country = {
name: string;
regions?: string[];
iso2: string;
countryCode: string;
dialCode: string;
format?: string;
priority: number;
mainCode?: boolean;
};

0 comments on commit 491e379

Please sign in to comment.