diff --git a/packages/intl-phone-input/src/component.tsx b/packages/intl-phone-input/src/component.tsx index e30e11c21a..1caaa75788 100644 --- a/packages/intl-phone-input/src/component.tsx +++ b/packages/intl-phone-input/src/component.tsx @@ -12,6 +12,7 @@ import { import { CountriesSelect } from './components'; import styles from './index.module.css'; import { formatPhoneWithUnclearableCountryCode } from './utils/format-phone-with-unclearable-country-code'; +import { useCaretAvoidCountryCode } from './useCaretAvoidCountryCode'; const countriesHash = getCountriesHash(); @@ -233,6 +234,11 @@ export const IntlPhoneInput = forwardRef( } }, [countryIso2, loadPhoneUtils, setCountryByDialCode, value]); + const country = countriesHash[countryIso2]; + const countryCodeLength = `+${country.dialCode}`.length; + + useCaretAvoidCountryCode({ inputRef, countryCodeLength, clearableCountryCode }); + return ( ; + countryCodeLength: number; + clearableCountryCode: boolean; +}; + +export function useCaretAvoidCountryCode({ + inputRef, + countryCodeLength, + clearableCountryCode, +}: Args) { + const input = inputRef.current; + + const moveCaretFromCountryCode = useCallback(() => { + if (!input) return; + + const selectionStart = input.selectionStart || 0; + + if (selectionStart < countryCodeLength) { + input.focus(); + input.setSelectionRange(countryCodeLength, countryCodeLength); + } + }, [input, countryCodeLength]); + + const preventCaretMovingOnCountryCode = useCallback( + (event: KeyboardEvent) => { + if (!input) return; + + const selectionStart = input.selectionStart || 0; + const toLeftKey = event.keyCode === 37; + + if (toLeftKey && selectionStart <= countryCodeLength) { + event.preventDefault(); + } + }, + [input, countryCodeLength], + ); + + useEffect(() => { + if (!input || clearableCountryCode) return; + + input.addEventListener('click', moveCaretFromCountryCode); + input.addEventListener('keydown', preventCaretMovingOnCountryCode); + + // eslint-disable-next-line consistent-return + return () => { + input.removeEventListener('click', moveCaretFromCountryCode); + input.removeEventListener('keydown', preventCaretMovingOnCountryCode); + }; + }, [clearableCountryCode, input, preventCaretMovingOnCountryCode, moveCaretFromCountryCode]); +} diff --git a/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.test.ts b/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.test.ts index fad55a5766..635abbebfc 100644 --- a/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.test.ts +++ b/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.test.ts @@ -2,31 +2,25 @@ import { Country } from '@alfalab/utils'; import { formatPhoneWithUnclearableCountryCode } from './format-phone-with-unclearable-country-code'; describe('formatPhoneWithUnclearableCountryCode', () => { - it('should work', () => { + it('shoul change 8 to +7 at russian country code', () => { const ru = { dialCode: '7', } as Country; + expect(formatPhoneWithUnclearableCountryCode('8171', ru)).toEqual('+7171'); + }); + + it('should work with long country code', () => { const az = { dialCode: '994', } as Country; - expect(formatPhoneWithUnclearableCountryCode('', ru)).toEqual('+7'); - expect(formatPhoneWithUnclearableCountryCode('+', ru)).toEqual('+7'); - expect(formatPhoneWithUnclearableCountryCode('7', ru)).toEqual('+7'); - expect(formatPhoneWithUnclearableCountryCode('+7', ru)).toEqual('+7'); - expect(formatPhoneWithUnclearableCountryCode('+17', ru)).toEqual('+71'); - expect(formatPhoneWithUnclearableCountryCode('+71', ru)).toEqual('+71'); - expect(formatPhoneWithUnclearableCountryCode('1+7', ru)).toEqual('+71'); expect(formatPhoneWithUnclearableCountryCode('', az)).toEqual('+994'); expect(formatPhoneWithUnclearableCountryCode('+', az)).toEqual('+994'); expect(formatPhoneWithUnclearableCountryCode('+9', az)).toEqual('+994'); expect(formatPhoneWithUnclearableCountryCode('+99', az)).toEqual('+994'); expect(formatPhoneWithUnclearableCountryCode('+994', az)).toEqual('+994'); - expect(formatPhoneWithUnclearableCountryCode('1+994', az)).toEqual('+9941'); - expect(formatPhoneWithUnclearableCountryCode('+1994', az)).toEqual('+9941'); - expect(formatPhoneWithUnclearableCountryCode('+9194', az)).toEqual('+9941'); - expect(formatPhoneWithUnclearableCountryCode('+9914', az)).toEqual('+9941'); + expect(formatPhoneWithUnclearableCountryCode('1+994', az)).toEqual('+994'); expect(formatPhoneWithUnclearableCountryCode('+9941', az)).toEqual('+9941'); }); }); diff --git a/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.ts b/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.ts index 83d6092321..c2241268b6 100644 --- a/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.ts +++ b/packages/intl-phone-input/src/utils/format-phone-with-unclearable-country-code.ts @@ -1,32 +1,21 @@ import { Country } from '@alfalab/utils'; -import { getPhoneDiff } from './get-phone-diff'; + +const RUSSIAN_DIAL_CODE = '7'; +const RUSSIAN_NATIONAL_DIAL_CODE = '8'; /** * Форматирует телефон с неудаляемым кодом страны */ export const formatPhoneWithUnclearableCountryCode = (phone: string, country: Country) => { - const defaultValue = `+${country.dialCode}`; - // При попытке стереть код страны возвращаем дефолтное значение - if (phone.length < defaultValue.length) { - return defaultValue; - } + const countryPrefix = `+${country.dialCode}`; - // Если код страны совпадает, даем вводить значение - if (phone.substr(1, country.dialCode.length) === country.dialCode) { + if (phone.startsWith(countryPrefix)) { return phone; } - const lengthDiff = phone.substr(1).length - country.dialCode.length; - // Если разница длины нового значения и длины кода страны равна 1, то определяем отличающийся символ и ставим его после кода - if (lengthDiff === 1) { - const diff = getPhoneDiff(phone, country); - // Если не смогли вычислить отличающийся символ, то возвращаем дефолтное значение - if (!diff) { - return defaultValue; - } - - return `+${country.dialCode}${diff}`; + if (country.dialCode === RUSSIAN_DIAL_CODE && phone.startsWith(RUSSIAN_NATIONAL_DIAL_CODE)) { + return phone.replace(RUSSIAN_NATIONAL_DIAL_CODE, countryPrefix); } - return phone; + return countryPrefix; }; diff --git a/packages/intl-phone-input/src/utils/get-phone-diff.test.ts b/packages/intl-phone-input/src/utils/get-phone-diff.test.ts deleted file mode 100644 index 2eadc54cf7..0000000000 --- a/packages/intl-phone-input/src/utils/get-phone-diff.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Country } from '@alfalab/utils'; -import { getPhoneDiff } from './get-phone-diff'; - -describe('getPhoneDiff', () => { - it('should work', () => { - const ru = { - dialCode: '7', - } as Country; - - const az = { - dialCode: '994', - } as Country; - expect(getPhoneDiff('', ru)).toEqual(null); - expect(getPhoneDiff('+', ru)).toEqual(null); - expect(getPhoneDiff('7', ru)).toEqual(null); - expect(getPhoneDiff('+7', ru)).toEqual(null); - expect(getPhoneDiff('+17', ru)).toEqual('1'); - expect(getPhoneDiff('1+7', ru)).toEqual('1'); - - expect(getPhoneDiff('', az)).toEqual(null); - expect(getPhoneDiff('+', az)).toEqual(null); - expect(getPhoneDiff('+9', az)).toEqual(null); - expect(getPhoneDiff('+99', az)).toEqual(null); - expect(getPhoneDiff('+994', az)).toEqual(null); - expect(getPhoneDiff('1+994', az)).toEqual('1'); - expect(getPhoneDiff('+1994', az)).toEqual('1'); - expect(getPhoneDiff('+9194', az)).toEqual('1'); - expect(getPhoneDiff('+9914', az)).toEqual('1'); - }); -}); diff --git a/packages/intl-phone-input/src/utils/get-phone-diff.ts b/packages/intl-phone-input/src/utils/get-phone-diff.ts deleted file mode 100644 index 43e2e7c3e3..0000000000 --- a/packages/intl-phone-input/src/utils/get-phone-diff.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Country } from '@alfalab/utils'; - -/** - * Возвращает разницу между значением и кодом страны - */ -export const getPhoneDiff = (value: string, country: Country) => { - if (!value || value === `+${country.dialCode}`) return null; - - const dialRegex = country.dialCode.split('').reduce((acc, item, index) => { - return `${acc}(?\\d+)*${item}`; - }, ''); - const regex = new RegExp(`(?\\d+)*\\+${dialRegex}`); - - const result = value.match(regex); - - if (!result) return null; - // eslint-disable-next-line no-prototype-builtins - if (!result.hasOwnProperty('groups')) return null; - const { groups } = result; - if (!groups) return null; - - return Object.values(groups).join(''); -};