UNPKG

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
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]))))); };