UNPKG

antd-phone-input

Version:

Advanced, highly customizable phone input component for Ant Design.

163 lines (162 loc) 12.8 kB
"use client"; "use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.locale = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const useFormInstance_1 = __importDefault(require("antd/es/form/hooks/useFormInstance")); const config_provider_1 = require("antd/es/config-provider"); const context_1 = require("antd/es/form/context"); const Form_1 = require("antd/es/form/Form"); const select_1 = __importDefault(require("antd/es/select")); const input_1 = __importDefault(require("antd/es/input")); const react_phone_hooks_1 = require("react-phone-hooks"); const locale_1 = __importDefault(require("./locale")); exports.locale = locale_1.default; const styles_1 = require("./styles"); const PhoneInput = (0, react_1.forwardRef)((_a, forwardedRef) => { var { value: initialValue = "", country = (0, react_phone_hooks_1.getDefaultISO2Code)(), distinct = false, disabled = false, enableArrow = false, enableSearch = false, disableDropdown = false, disableParentheses = false, onlyCountries = [], excludeCountries = [], preferredCountries = [], searchNotFound: defaultSearchNotFound = "No country found", searchPlaceholder: defaultSearchPlaceholder = "Search country", dropdownRender = (node) => node, onMount: handleMount = () => null, onInput: handleInput = () => null, onChange: handleChange = () => null, onKeyDown: handleKeyDown = () => null } = _a, antInputProps = __rest(_a, ["value", "country", "distinct", "disabled", "enableArrow", "enableSearch", "disableDropdown", "disableParentheses", "onlyCountries", "excludeCountries", "preferredCountries", "searchNotFound", "searchPlaceholder", "dropdownRender", "onMount", "onInput", "onChange", "onKeyDown"]); const formInstance = (0, useFormInstance_1.default)(); const { locale = {}, getPrefixCls } = (0, react_1.useContext)(config_provider_1.ConfigContext); const formContext = (0, react_1.useContext)(context_1.FormContext); const inputRef = (0, react_1.useRef)(null); const searchRef = (0, react_1.useRef)(null); const selectedRef = (0, react_1.useRef)(false); const initiatedRef = (0, react_1.useRef)(false); const [query, setQuery] = (0, react_1.useState)(""); const [minWidth, setMinWidth] = (0, react_1.useState)(0); const [countryCode, setCountryCode] = (0, react_1.useState)(country); const { locale: localeIdentifier, searchNotFound = defaultSearchNotFound, searchPlaceholder = defaultSearchPlaceholder, countries = new Proxy({}, ({ get: (_, prop) => prop })), } = locale.PhoneInput || {}; const prefixCls = getPrefixCls(); (0, styles_1.injectMergedStyles)(prefixCls); const { value, pattern, metadata, setValue, countriesList, } = (0, react_phone_hooks_1.usePhone)({ query, country, distinct, countryCode, initialValue, onlyCountries, excludeCountries, preferredCountries, disableParentheses, locale: localeIdentifier, }); const { onInput: onInputMaskHandler, onKeyDown: onKeyDownMaskHandler, } = (0, react_phone_hooks_1.useMask)(pattern); const selectValue = (0, react_1.useMemo)(() => { var _a, _b; let metadata = (0, react_phone_hooks_1.getMetadata)((0, react_phone_hooks_1.getRawValue)(value), countriesList); metadata = metadata || (0, react_phone_hooks_1.getCountry)(countryCode); return ((_a = (Object.assign({}, metadata))) === null || _a === void 0 ? void 0 : _a[0]) + ((_b = (Object.assign({}, metadata))) === null || _b === void 0 ? void 0 : _b[2]); }, [countriesList, countryCode, value]); const namePath = (0, react_1.useMemo)(() => { let path = []; let formName = (formContext === null || formContext === void 0 ? void 0 : formContext.name) || ""; let fieldName = (antInputProps === null || antInputProps === void 0 ? void 0 : antInputProps.id) || ""; if (formName) { path.push(formName); fieldName = fieldName.slice(formName.length + 1); } return path.concat(fieldName.split("_")); }, [antInputProps, formContext]); const phoneValue = (0, Form_1.useWatch)(namePath, formInstance); const setFieldValue = (0, react_1.useCallback)((value) => { if (formInstance) formInstance.setFieldValue(namePath, value); }, [formInstance, namePath]); const onKeyDown = (0, react_1.useCallback)((event) => { onKeyDownMaskHandler(event); handleKeyDown(event); }, [handleKeyDown, onKeyDownMaskHandler]); const onChange = (0, react_1.useCallback)((event) => { const formattedNumber = selectedRef.current ? event.target.value : (0, react_phone_hooks_1.getFormattedNumber)(event.target.value, pattern); selectedRef.current = false; const phoneMetadata = (0, react_phone_hooks_1.parsePhoneNumber)(formattedNumber, countriesList); setCountryCode(phoneMetadata.isoCode); setValue(formattedNumber); setQuery(""); handleChange(Object.assign(Object.assign({}, phoneMetadata), { valid: (strict) => (0, react_phone_hooks_1.checkValidity)(phoneMetadata, strict) }), event); }, [countriesList, handleChange, pattern, setValue]); const onInput = (0, react_1.useCallback)((event) => { onInputMaskHandler(event); handleInput(event); }, [onInputMaskHandler, handleInput]); const onMount = (0, react_1.useCallback)((value) => { setFieldValue(value); handleMount(value); }, [handleMount, setFieldValue]); const onDropdownVisibleChange = (0, react_1.useCallback)((open) => { if (open && enableSearch) setTimeout(() => searchRef.current.focus(), 100); }, [enableSearch]); const ref = (0, react_1.useCallback)((node) => { [forwardedRef, inputRef].forEach((ref) => { if (typeof ref === "function") ref(node); else if (ref != null) ref.current = node; }); }, [forwardedRef]); (0, react_1.useEffect)(() => { const rawValue = (0, react_phone_hooks_1.getRawValue)(phoneValue); const metadata = (0, react_phone_hooks_1.getMetadata)(rawValue); // Skip if value has not been updated by `setFieldValue`. if (!(metadata === null || metadata === void 0 ? void 0 : metadata[3]) || rawValue === (0, react_phone_hooks_1.getRawValue)(value)) return; const formattedNumber = (0, react_phone_hooks_1.getFormattedNumber)(rawValue, metadata === null || metadata === void 0 ? void 0 : metadata[3]); const phoneMetadata = (0, react_phone_hooks_1.parsePhoneNumber)(formattedNumber); setFieldValue(Object.assign(Object.assign({}, phoneMetadata), { valid: (strict) => (0, react_phone_hooks_1.checkValidity)(phoneMetadata, strict) })); setCountryCode(metadata === null || metadata === void 0 ? void 0 : metadata[0]); setValue(formattedNumber); }, [phoneValue, value, setFieldValue, setValue]); (0, react_1.useEffect)(() => { if (initiatedRef.current) return; initiatedRef.current = true; let initialValue = (0, react_phone_hooks_1.getRawValue)(value); if (!initialValue.startsWith(metadata === null || metadata === void 0 ? void 0 : metadata[2])) { initialValue = metadata === null || metadata === void 0 ? void 0 : metadata[2]; } const formattedNumber = (0, react_phone_hooks_1.getFormattedNumber)(initialValue, pattern); const phoneMetadata = (0, react_phone_hooks_1.parsePhoneNumber)(formattedNumber, countriesList); onMount(Object.assign(Object.assign({}, phoneMetadata), { valid: (strict) => (0, react_phone_hooks_1.checkValidity)(phoneMetadata, strict) })); setCountryCode(phoneMetadata.isoCode); setValue(formattedNumber); }, [countriesList, metadata, onMount, pattern, setValue, value]); const suffixIcon = (0, react_1.useMemo)(() => { return enableArrow && ((0, jsx_runtime_1.jsx)("span", { role: "img", className: "anticon", style: { paddingLeft: 8 }, children: (0, jsx_runtime_1.jsx)("svg", { className: "icon", viewBox: "0 0 1024 1024", focusable: "false", fill: "currentColor", width: "16", height: "18", children: (0, jsx_runtime_1.jsx)("path", { d: "M848 368a48 48 0 0 0-81.312-34.544l-0.016-0.016-254.784 254.784-251.488-251.488a48 48 0 1 0-71.04 64.464l-0.128 0.128 288 288 0.016-0.016a47.84 47.84 0 0 0 34.544 14.688h0.224a47.84 47.84 0 0 0 34.544-14.688l0.016 0.016 288-288-0.016-0.016c8.32-8.624 13.44-20.368 13.44-33.312z" }) }) })); }, [enableArrow]); const countriesSelect = (0, react_1.useMemo)(() => ((0, jsx_runtime_1.jsxs)(select_1.default, { suffixIcon: null, value: selectValue, disabled: disabled, open: disableDropdown ? false : undefined, onSelect: (selectedOption, { key }) => { const [_, mask] = key.split("_"); const selectedCountryCode = selectedOption.slice(0, 2); const formattedNumber = (0, react_phone_hooks_1.displayFormat)((0, react_phone_hooks_1.cleanInput)(mask, mask).join("")); const phoneMetadata = (0, react_phone_hooks_1.parsePhoneNumber)(formattedNumber, countriesList, selectedCountryCode); setFieldValue(Object.assign(Object.assign({}, phoneMetadata), { valid: (strict) => (0, react_phone_hooks_1.checkValidity)(phoneMetadata, strict) })); setCountryCode(selectedCountryCode); setValue(formattedNumber); setQuery(""); selectedRef.current = true; const nativeInputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set; nativeInputValueSetter.call(inputRef.current.input, formattedNumber); inputRef.current.input.dispatchEvent(new Event("change", { bubbles: true })); inputRef.current.input.focus(); }, optionLabelProp: "label", dropdownStyle: { minWidth }, onDropdownVisibleChange: onDropdownVisibleChange, dropdownRender: (menu) => ((0, jsx_runtime_1.jsxs)("div", { className: `${prefixCls}-phone-input-search-wrapper`, children: [enableSearch && ((0, jsx_runtime_1.jsx)(input_1.default, { value: query, ref: searchRef, placeholder: searchPlaceholder, onInput: ({ target }) => setQuery(target.value) })), countriesList.length ? menu : ((0, jsx_runtime_1.jsx)("div", { className: "ant-select-item-empty", children: searchNotFound }))] })), children: [(0, jsx_runtime_1.jsx)(select_1.default.Option, { children: null, value: selectValue, style: { display: "none" }, label: (0, jsx_runtime_1.jsxs)("div", { style: { display: "flex" }, children: [(0, jsx_runtime_1.jsx)("div", { className: `flag ${countryCode}` }), suffixIcon] }) }, `${countryCode}_default`), countriesList.map(([iso, name, dial, pattern]) => { const mask = disableParentheses ? pattern.replace(/[()]/g, "") : pattern; return ((0, jsx_runtime_1.jsx)(select_1.default.Option, { value: iso + dial, label: (0, jsx_runtime_1.jsxs)("div", { style: { display: "flex" }, children: [(0, jsx_runtime_1.jsx)("div", { className: `flag ${iso}` }), suffixIcon] }), children: (0, jsx_runtime_1.jsxs)("div", { className: `${prefixCls}-phone-input-select-item`, children: [(0, jsx_runtime_1.jsx)("div", { className: `flag ${iso}` }), countries[name], "\u00A0", (0, react_phone_hooks_1.displayFormat)(mask)] }) }, `${iso}_${mask}`)); })] })), [selectValue, suffixIcon, countryCode, query, disabled, disableParentheses, disableDropdown, onDropdownVisibleChange, minWidth, searchNotFound, countries, countriesList, setFieldValue, setValue, prefixCls, enableSearch, searchPlaceholder]); return ((0, jsx_runtime_1.jsx)("div", { className: `${prefixCls}-phone-input-wrapper`, ref: node => setMinWidth((node === null || node === void 0 ? void 0 : node.offsetWidth) || 0), children: (0, jsx_runtime_1.jsx)(input_1.default, Object.assign({ ref: ref, inputMode: "tel", value: value, onInput: onInput, onChange: onChange, onKeyDown: onKeyDown, addonBefore: dropdownRender(countriesSelect), disabled: disabled }, antInputProps)) })); }); exports.default = PhoneInput;