UNPKG

mui-phone-input

Version:

Advanced, highly customizable phone input component for Material UI.

115 lines (114 loc) 8.81 kB
"use client"; 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; }; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { InputAdornment, MenuItem, Select, TextField, useThemeProps } from "@mui/material"; import { checkValidity, displayFormat, getCountry, getDefaultISO2Code, getFormattedNumber, getMetadata, getRawValue, parsePhoneNumber, useMask, usePhone, } from "react-phone-hooks"; import locale from "./locale"; import { injectMergedStyles } from "./styles"; injectMergedStyles(); const PhoneInput = forwardRef((_a, forwardedRef) => { var { value: initialValue = "", variant = undefined, searchVariant = undefined, country = 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", onMount: handleMount = () => null, onInput: handleInput = () => null, onChange: handleChange = () => null, onKeyDown: handleKeyDown = () => null } = _a, muiInputProps = __rest(_a, ["value", "variant", "searchVariant", "country", "distinct", "disabled", "enableArrow", "enableSearch", "disableDropdown", "disableParentheses", "onlyCountries", "excludeCountries", "preferredCountries", "searchNotFound", "searchPlaceholder", "onMount", "onInput", "onChange", "onKeyDown"]); searchVariant = searchVariant || variant; const inputRef = useRef(null); const searchRef = useRef(false); const initiatedRef = useRef(false); const [query, setQuery] = useState(""); const [open, setOpen] = useState(false); const [maxWidth, setMaxWidth] = useState(0); const [countryCode, setCountryCode] = useState(country); const { locale, searchNotFound = defaultSearchNotFound, searchPlaceholder = defaultSearchPlaceholder, countries = new Proxy({}, ({ get: (_, prop) => prop })), } = useThemeProps({ props: {}, name: "MuiPhoneInput" }); const { value, pattern, metadata, setValue, countriesList, } = usePhone({ query, locale, country, distinct, countryCode, initialValue, onlyCountries, excludeCountries, preferredCountries, disableParentheses, }); const { onInput: onInputMaskHandler, onKeyDown: onKeyDownMaskHandler, } = useMask(pattern); const selectValue = useMemo(() => { var _a, _b; let metadata = getMetadata(getRawValue(value), countriesList); metadata = metadata || 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 onKeyDown = useCallback((event) => { onKeyDownMaskHandler(event); handleKeyDown(event); }, [handleKeyDown, onKeyDownMaskHandler]); const onChange = useCallback((event) => { const formattedNumber = getFormattedNumber(event.target.value, pattern); const phoneMetadata = parsePhoneNumber(formattedNumber, countriesList); setCountryCode(phoneMetadata.isoCode); setValue(formattedNumber); handleChange(Object.assign(Object.assign({}, phoneMetadata), { valid: (strict) => checkValidity(phoneMetadata, strict) }), event); }, [countriesList, handleChange, pattern, setValue]); const onInput = useCallback((event) => { onInputMaskHandler(event); handleInput(event); }, [handleInput, onInputMaskHandler]); const onMount = useCallback((value) => { handleMount(value); }, [handleMount]); const ref = useCallback((node) => { [forwardedRef, inputRef].forEach((ref) => { if (typeof ref === "function") ref(node); else if (ref != null) ref.current = node; }); }, [forwardedRef]); useEffect(() => { if (initiatedRef.current) return; initiatedRef.current = true; let initialValue = 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 = getFormattedNumber(initialValue, pattern); const phoneMetadata = parsePhoneNumber(formattedNumber, countriesList); onMount(Object.assign(Object.assign({}, phoneMetadata), { valid: (strict) => checkValidity(phoneMetadata, strict) })); setCountryCode(phoneMetadata.isoCode); setValue(formattedNumber); }, [countriesList, metadata, onMount, pattern, setValue, value]); return (_jsxs("div", { className: "mui-phone-input-wrapper", ref: node => setMaxWidth((node === null || node === void 0 ? void 0 : node.offsetWidth) || 0), children: [(!disableDropdown && !disabled) && (_jsx(Select, { open: open, variant: variant, value: selectValue, onClose: () => setOpen(searchRef.current), style: { position: "absolute", top: 0, left: 0, visibility: "hidden", width: "100%", zIndex: -1 }, children: _jsxs("div", { className: "mui-phone-input-search-wrapper", onKeyDown: (e) => e.stopPropagation(), children: [enableSearch && (_jsx(TextField, { autoFocus: true, type: "search", value: query, variant: searchVariant, placeholder: searchPlaceholder, className: "mui-phone-input-search", onChange: (e) => setQuery(e.target.value), onBlur: () => searchRef.current = false, onFocus: () => searchRef.current = true })), _jsx("div", { className: "mui-phone-input-search-list", children: countriesList.length ? countriesList.map(([iso, name, dial, pattern]) => { const mask = disableParentheses ? pattern.replace(/[()]/g, "") : pattern; return (_jsx(MenuItem, { disableRipple: true, value: iso + dial, style: { maxWidth }, selected: selectValue === iso + dial, onClick: () => { const formattedNumber = getFormattedNumber(mask, mask); const input = inputRef.current.querySelector("input"); input.value = formattedNumber; setValue(formattedNumber); setCountryCode(iso); setQuery(""); const nativeInputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set; nativeInputValueSetter.call(input, formattedNumber); input.dispatchEvent(new Event("change", { bubbles: true })); setTimeout(() => input.focus(), 100); }, children: _jsxs("div", { className: "mui-phone-input-select-item", children: [_jsx("div", { className: `flag ${iso}` }), _jsxs("div", { className: "label", children: [countries[name], "\u00A0", displayFormat(mask)] })] }) }, iso + mask)); }) : _jsx(MenuItem, { disabled: true, children: searchNotFound }) })] }) })), _jsx(TextField, Object.assign({ ref: ref, type: "tel", value: value, variant: variant, onInput: onInput, disabled: disabled, onChange: onChange, onKeyDown: onKeyDown, InputProps: { startAdornment: (_jsx(InputAdornment, { position: "start", children: _jsxs("span", { style: { display: "flex", cursor: "pointer", alignItems: "center", justifyContent: "center", }, onClick: () => setOpen(!open), children: [_jsx("div", { className: `flag ${countryCode}` }), enableArrow && (_jsx("svg", { viewBox: "0 0 24 24", focusable: "false", fill: "currentColor", style: { paddingLeft: 4 }, width: "22", height: "20", children: _jsx("path", { d: "M7.41 8.59 12 13.17l4.59-4.58L18 10l-6 6-6-6z" }) }))] }) })) } }, muiInputProps))] })); }); export default PhoneInput; export { locale };