react-phone-input-pro
Version:
Phone number input component for react to format phone numbers according to selected countries in real time.
380 lines (379 loc) • 19 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import React, { useEffect, useRef } from 'react';
import { useState } from 'react';
import { c } from '../data/countries';
import { getSeperatorsPositions, getDefaultCountry, compare } from '../utils/methods';
// import '../styles/index.css'
import { CountrySelector } from './CountrySelector';
import { onInputFocus } from '../utils/stylingMethods';
import { FORM_CLASS, PHONE_INPUT_CLASS, BORDER_RED } from '../utils/cssClassNames';
export const PhoneInput = (props) => {
const [newCountries] = useState([...c].sort(compare));
const [defaultCountry] = useState(getDefaultCountry(newCountries, c, props.defaultCountry, props.onlyCountries, String(props.value), props.initialFormat, props.includeDialingCode));
const [countryCode, setCountryCode] = useState(() => {
if (props.format !== undefined) {
const { prefix } = getSeperatorsPositions(props.format);
return prefix;
}
else {
return defaultCountry.d;
}
});
const drpButton = useRef();
const list = useRef();
const [format, setFormat] = useState({
format: props.format ? props.format : defaultCountry.f,
placeholder: props.placeholder ? props.placeholder : defaultCountry.p,
});
const [fixedLength] = useState(props.fixLength || props.fixLength === undefined ? true : false);
const inputElm = useRef();
const parent = useRef();
props.getCountryCode && props.getCountryCode(countryCode);
useEffect(() => {
var _a;
if (props.className && parent.current) {
(_a = parent.current) === null || _a === void 0 ? void 0 : _a.classList.add(props.className);
}
if (inputElm.current && parent.current) {
if (drpButton.current && list.current) {
onInputFocus(inputElm.current, parent.current, drpButton.current, list.current);
}
else {
onInputFocus(inputElm.current, parent.current);
}
getRawNumber(inputElm.current.value);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [format]);
useEffect(() => {
if (props.error && props.error.length && props.error[0] && parent.current) {
parent.current.classList.add(BORDER_RED);
}
else {
parent.current && parent.current.classList.remove(BORDER_RED);
}
}, []);
const ondown = (key, e) => __awaiter(void 0, void 0, void 0, function* () {
var _a;
const { seperators, prefixIndexes, prefix, justSymbols } = getSeperatorsPositions(format.format);
const validKeys = /([0-9]|Backspace|ArrowLeft|ArrowRight|Control|v|c|x)/g;
const validateNumber = /[0-9]/g;
const isValidKey = key.match(validKeys);
const isValidNumber = key.match(validateNumber);
const text = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString();
const addNum = () => {
if (e.currentTarget.selectionStart || e.currentTarget.selectionStart === 0) {
if (!fixedLength) {
if (text && (text === null || text === void 0 ? void 0 : text.length)) {
e.currentTarget.setRangeText(key, e.currentTarget.selectionStart, e.currentTarget.selectionStart + text.length, 'preserve');
updateNumber(e.currentTarget);
}
for (let i = 0; i < seperators.length; i++) {
if (e.currentTarget.selectionStart === seperators[i].index) {
e.currentTarget.setRangeText(seperators[i].symbol, e.currentTarget.selectionStart, e.currentTarget.selectionStart + seperators[i].symbol.length, 'end');
}
}
e.currentTarget.setRangeText(key, e.currentTarget.selectionStart, e.currentTarget.selectionStart + 1, 'end');
}
else if (e.currentTarget.selectionStart < format.format.length) {
if (text && (text === null || text === void 0 ? void 0 : text.length)) {
e.currentTarget.setRangeText(key, e.currentTarget.selectionStart, e.currentTarget.selectionStart + text.length, 'preserve');
updateNumber(e.currentTarget);
}
for (let i = 0; i < seperators.length; i++) {
if (e.currentTarget.selectionStart === seperators[i].index) {
e.currentTarget.setRangeText(seperators[i].symbol, e.currentTarget.selectionStart, e.currentTarget.selectionStart + seperators[i].symbol.length, 'end');
}
}
e.currentTarget.setRangeText(key, e.currentTarget.selectionStart, e.currentTarget.selectionStart + 1, 'end');
}
}
};
if (isValidKey) {
if (key === 'Backspace') {
if (e.currentTarget.selectionEnd && !prefixIndexes.includes(e.currentTarget.selectionEnd - 1)) {
if (e.currentTarget.selectionStart && e.currentTarget.selectionStart <= prefix.length) {
e.currentTarget.selectionStart = prefix.length;
}
if (e.currentTarget.selectionStart || e.currentTarget.selectionStart === 0) {
if (e.currentTarget.selectionStart !== e.currentTarget.selectionEnd) {
e.currentTarget.setRangeText('', e.currentTarget.selectionStart, e.currentTarget.selectionEnd, 'end');
updateNumber(e.currentTarget);
}
else {
e.currentTarget.setRangeText('', e.currentTarget.selectionEnd - 1, e.currentTarget.selectionEnd, 'end');
updateNumber(e.currentTarget);
}
getRawNumber(e.currentTarget.value);
}
}
}
if (isValidNumber) {
addNum();
getRawNumber(e.currentTarget.value);
e.preventDefault();
}
else if (e.currentTarget.selectionEnd &&
key === 'ArrowLeft' &&
justSymbols.includes(e.currentTarget.value[e.currentTarget.selectionEnd - 2]) &&
e.currentTarget.selectionEnd > prefix.length) {
e.currentTarget.selectionEnd = e.currentTarget.selectionEnd - 1;
}
else if (e.currentTarget.selectionEnd && key === 'ArrowLeft' && e.currentTarget.selectionEnd < prefix.length) {
e.preventDefault();
}
else if (e.currentTarget.selectionStart &&
key === 'ArrowRight' &&
justSymbols.includes(e.currentTarget.value[e.currentTarget.selectionStart + 1]) &&
e.currentTarget.selectionStart > prefix.length) {
e.currentTarget.selectionStart = e.currentTarget.selectionStart + 2;
}
else if (!e.ctrlKey && !e.metaKey && key !== 'ArrowLeft' && key !== 'ArrowRight') {
e.preventDefault();
}
}
else {
e.preventDefault();
}
});
const arrangeNumber = (value) => {
let trimmedText = '';
const { seperators, prefix } = getSeperatorsPositions(format.format);
if (value && prefix.length && value.includes(prefix)) {
const slicedValue = value.slice(prefix.length, value.length);
const slicedDividers = seperators.slice(prefix.length, seperators.length);
for (let i = 0; i < slicedDividers.length; i++) {
slicedDividers[i].index = slicedDividers[i].index - prefix.length;
}
for (let i = 0; i < slicedValue.length; i++) {
if ((Number(slicedValue[i]) && slicedValue[i] !== '+' && slicedValue[i] !== '-' && slicedValue[i] !== '.') ||
slicedValue[i] === '0') {
trimmedText += slicedValue[i];
}
}
}
else {
if (value) {
for (let i = 0; i < value.length; i++) {
if ((Number(value[i]) && value[i] !== '+' && value[i] !== '-' && value[i] !== '.') || value[i] === '0') {
trimmedText += value[i];
}
}
}
}
let dividersLength = 0;
for (let i = 0; i < seperators.length; i++) {
dividersLength += seperators[i].symbol.length;
}
const slice = fixedLength ? trimmedText.slice(0, format.format.length - dividersLength) : trimmedText;
const split = Array.from(slice);
return seperateNumbers(split);
};
const seperateNumbers = (value) => {
const { seperators, prefix } = getSeperatorsPositions(format.format);
let j = 0;
for (let i = 0; i < value.length; i++) {
if (seperators[j]) {
if (i === seperators[j].index) {
value.splice(seperators[j].index, 0, seperators[j].symbol);
j++;
}
}
}
let modifiedNumber;
if (props.prefix) {
if (!value.length && prefix) {
if (prefix[prefix.length] === '(') {
const val = prefix.slice(0, prefix.length - 1);
modifiedNumber = val;
}
else if (prefix[prefix.length - 1] === '(') {
const val = prefix.slice(0, prefix.length - 2);
modifiedNumber = val;
}
else {
modifiedNumber = prefix;
}
}
else {
modifiedNumber = value.join('');
}
return modifiedNumber;
}
modifiedNumber = value.join('');
return modifiedNumber;
};
const getRawNumber = (value) => {
const { seperators, prefix } = getSeperatorsPositions(format.format);
if (prefix.length && value.includes(prefix)) {
const slicedValue = value.slice(prefix.length, value.length);
const slicedDividers = seperators.slice(prefix.length, seperators.length);
for (let i = 0; i < slicedDividers.length; i++) {
slicedDividers[i].index = slicedDividers[i].index - prefix.length;
}
const valAray = Array.from(slicedValue);
let newValue = '';
for (let i = 0; i < valAray.length; i++) {
if (slicedDividers.length) {
if (i === slicedDividers[0].index) {
valAray.slice(slicedDividers[0].index, 0);
slicedDividers.shift();
}
else {
newValue += valAray[i];
}
}
else {
newValue += valAray[i];
}
}
if ((props.includeDialingCode === undefined || props.includeDialingCode) && newValue.length) {
props.onchange && props.onchange(countryCode.slice(1, countryCode.length) + newValue);
}
else {
props.onchange && props.onchange(newValue);
}
}
else {
const valAray = Array.from(value);
let newValue = '';
for (let i = 0; i < valAray.length; i++) {
if (seperators.length) {
if (i === seperators[0].index) {
valAray.slice(seperators[0].index, 0);
seperators.shift();
}
else {
newValue += valAray[i];
}
}
else {
newValue += valAray[i];
}
}
if ((props.includeDialingCode === undefined || props.includeDialingCode) && newValue.length) {
props.onchange && props.onchange(countryCode.slice(1, countryCode.length) + newValue);
}
else {
props.onchange && props.onchange(newValue);
}
}
};
const onpaste = (e) => __awaiter(void 0, void 0, void 0, function* () {
var _b;
let start = e.currentTarget.selectionStart;
const target = e.currentTarget;
const copiedText = yield e.clipboardData.getData('Text');
const value = copiedText;
const text = (_b = window.getSelection()) === null || _b === void 0 ? void 0 : _b.toString();
const { prefix } = getSeperatorsPositions(format.format);
let trimmedText = '';
for (let i = 0; i < value.length; i++) {
if ((Number(value[i]) && value[i] !== '+' && value[i] !== '-' && value[i] !== '.') || value[i] === '0') {
trimmedText += value[i];
}
}
if (start <= prefix.length) {
target.setRangeText(prefix, 0, prefix.length, 'end');
start = prefix.length;
}
if (text === null || text === void 0 ? void 0 : text.length) {
target.setRangeText(trimmedText, start, start + text.length, 'end');
updateNumber(target);
}
else {
target.setRangeText(trimmedText, start, start, 'end');
updateNumber(target);
}
getRawNumber(target.value);
e.preventDefault();
});
const oncut = (e) => __awaiter(void 0, void 0, void 0, function* () {
const start = e.currentTarget.selectionStart;
const end = e.currentTarget.selectionEnd;
const value = e.currentTarget.value;
if (start && end) {
const copiedText = e.currentTarget.value.slice(start, end);
navigator.clipboard.writeText(copiedText);
const newValue = value.substring(0, start) + value.substring(end, value.length);
e.currentTarget.value = newValue;
e.currentTarget.selectionStart = start;
e.currentTarget.selectionEnd = start;
}
updateNumber(e.currentTarget);
getRawNumber(e.currentTarget.value);
e.preventDefault();
});
const onclick = (e) => {
const { justSymbols } = getSeperatorsPositions(format.format);
const { prefix } = getSeperatorsPositions(format.format);
if (prefix &&
(e.currentTarget.selectionStart || e.currentTarget.selectionStart === 0) &&
e.currentTarget.selectionStart < prefix.length) {
if (e.currentTarget.value[prefix.length - 1] === '(') {
e.currentTarget.selectionStart = prefix.length - 1;
}
else {
e.currentTarget.selectionStart = prefix.length;
}
}
else if (prefix &&
e.currentTarget.selectionEnd &&
justSymbols.includes(e.currentTarget.value[e.currentTarget.selectionEnd - 1]) &&
e.currentTarget.selectionEnd > prefix.length)
e.currentTarget.selectionEnd = e.currentTarget.selectionEnd - 1;
};
const updateNumber = (target) => {
const { prefix } = getSeperatorsPositions(format.format);
const getStart = target.selectionStart;
const getEnd = target.selectionEnd;
target.value = arrangeNumber(target.value);
if (target.value.length === prefix.length) {
target.selectionEnd = prefix.length;
target.selectionStart = prefix.length;
}
else {
if (getEnd && getStart) {
target.selectionEnd = getEnd;
target.selectionStart = getStart;
}
}
};
const inputValue = (value) => {
let text = String(value);
if (props.includeDialingCode === undefined || props.includeDialingCode) {
if (props.format) {
const pre = countryCode.slice(1, countryCode.length);
if (text.startsWith(pre)) {
text = value.toString().slice(countryCode.length - 1, text.length);
return arrangeNumber(text);
}
}
for (let i = 0; i < newCountries.length; i++) {
const pre = newCountries[i].d.slice(1, newCountries[i].d.length);
if (text.startsWith(pre)) {
text = value.toString().slice(newCountries[i].d.length - 1, text.length);
return arrangeNumber(text);
}
}
}
return arrangeNumber(text);
};
const inputStyle = props.format
? { borderRadius: '4px', margin: '0.375rem 0.2rem' }
: { borderRadius: '0px 4px 4px 0px' };
return (React.createElement(React.Fragment, null,
React.createElement("div", { className: FORM_CLASS, ref: (ref) => (parent.current = ref) },
React.createElement("label", null, !props.format && (React.createElement(CountrySelector, { disabled: props.disabled, fullIsoCode: props.fullIsoCode, searchOption: props.searchOption, defaultCountry: defaultCountry, onlyCountries: props.onlyCountries, setCountryCode: (code) => setCountryCode(code), setFormat: (value) => setFormat(value), flags: props.flags, input: parent.current, mainInput: inputElm.current, drpButton: (btn) => (drpButton.current = btn), list: (l) => (list.current = l) }))),
React.createElement("input", Object.assign({ ref: (ref) => (inputElm.current = ref), disabled: props.disabled, type: 'tel', autoComplete: 'off', className: PHONE_INPUT_CLASS, placeholder: format.placeholder, style: inputStyle, value: inputValue(props.value), onKeyDown: (e) => ondown(e.key, e), onPaste: (e) => onpaste(e), onChange: (e) => updateNumber(e.currentTarget), onCut: (e) => oncut(e), onClick: (e) => onclick(e), name: props.name, onBlur: props.onBlur }, props.register))),
props.error && (React.createElement("div", { style: { textAlign: 'left', color: 'red', fontSize: 'small', marginTop: '5px' } },
React.createElement("p", null, props.error.length > 1 && props.error[0] && props.error[1])))));
};