UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

424 lines (423 loc) 17.3 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var z = _interopRequireWildcard(require("zod")); var _index = require("../../../../components/index.js"); var _clsx = _interopRequireDefault(require("clsx")); var _useCountries = _interopRequireDefault(require("../SelectCountry/useCountries.js")); var _index2 = _interopRequireDefault(require("../String/index.js")); var _index3 = _interopRequireDefault(require("../Composition/index.js")); var _index4 = require("../../hooks/index.js"); var _utils = require("../../../../components/flex/utils.js"); var _Context = _interopRequireDefault(require("../../../../shared/Context.js")); var _index5 = require("../SelectCountry/index.js"); var _detectCountryCode = _interopRequireDefault(require("../../../../shared/detectCountryCode.js")); var _useTranslation = _interopRequireDefault(require("../../hooks/useTranslation.js")); var _withComponentMarkers = _interopRequireDefault(require("../../../../shared/helpers/withComponentMarkers.js")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } const defaultCountryCode = '+47'; const defaultPlaceholder = '00 00 00 00'; const defaultMask = [/\d/, /\d/, ' ', /\d/, /\d/, ' ', /\d/, /\d/, ' ', /\d/, /\d/]; function PhoneNumber(props = {}) { var _sharedContext$locale, _props$inputRef, _countryCodeRef$curre2; const sharedContext = (0, _react.useContext)(_Context.default); const { numberLabel: defaultLabel, countryCodeLabel: defaultCountryCodeLabel, errorRequired } = (0, _useTranslation.default)().PhoneNumber; const lang = (_sharedContext$locale = sharedContext.locale) === null || _sharedContext$locale === void 0 ? void 0 : _sharedContext$locale.split('-')[0]; const countryCodeRef = (0, _react.useRef)(props.emptyValue); const prevCountryCodeRef = (0, _react.useRef)(countryCodeRef.current); const numberRef = (0, _react.useRef)(props.emptyValue); const dataRef = (0, _react.useRef)(null); const langRef = (0, _react.useRef)(lang); const wasFilled = (0, _react.useRef)(false); const currentCountryRef = (0, _react.useRef)(undefined); const errorMessages = (0, _react.useMemo)(() => ({ 'Field.errorRequired': errorRequired, 'Field.errorPattern': errorRequired, ...props.errorMessages }), [errorRequired, props.errorMessages]); const validateRequired = (0, _react.useCallback)((value, { required, isChanged, error }) => { if (required) { const [countryCode, phoneNumber] = splitValue(value); if (countryCode !== prevCountryCodeRef.current) { if (countryCode) { prevCountryCodeRef.current = countryCode; } return undefined; } if (isChanged && !phoneNumber) { return error; } } return undefined; }, []); const fromExternal = (0, _react.useCallback)(external => { if (typeof external === 'string') { const [countryCode, phoneNumber] = splitValue(external); if (!countryCode && !phoneNumber && !props.omitCountryCodeField) { return countryCodeRef.current; } if (countryCode && phoneNumber) { return toE164([countryCode, phoneNumber]); } } return external; }, [props.omitCountryCodeField]); const toEvent = (0, _react.useCallback)(value => { const [, phoneNumber] = splitValue(value); if (!phoneNumber) { return props.emptyValue; } return value; }, [props.emptyValue]); const customTransformIn = props.transformIn; const transformIn = (0, _react.useCallback)(value => { if (customTransformIn) { const external = customTransformIn(value); if (typeof external === 'string') { return external; } if (external !== null && external !== void 0 && external.phoneNumber) { return toE164([external.countryCode, external.phoneNumber]); } } return value; }, [customTransformIn]); const provideAdditionalArgs = (0, _react.useCallback)(value => { var _currentCountryRef$cu; const [countryCode, phoneNumber] = splitValue(value); return { ...(!props.omitCountryCodeField ? { countryCode: countryCode || countryCodeRef.current || undefined } : {}), phoneNumber: phoneNumber || numberRef.current || undefined, iso: (_currentCountryRef$cu = currentCountryRef.current) === null || _currentCountryRef$cu === void 0 ? void 0 : _currentCountryRef$cu.iso }; }, [props.omitCountryCodeField]); const schema = (0, _react.useMemo)(() => { if (props.schema) { return props.schema; } if (!props.pattern) return undefined; return p => { let s = z.string(); if (p !== null && p !== void 0 && p.pattern) { try { s = s.regex(new RegExp(p.pattern, 'u'), 'Field.errorPattern'); } catch (_e) {} } return s; }; }, [props.schema, props.pattern]); const defaultProps = { ...(schema ? { schema } : {}), errorMessages }; const ref = (0, _react.useRef)(undefined); const preparedProps = { ...props, ...defaultProps, validateRequired, fromExternal, toEvent, provideAdditionalArgs, transformIn, inputRef: (_props$inputRef = props.inputRef) !== null && _props$inputRef !== void 0 ? _props$inputRef : ref }; const { id, path, itemPath, value, className, inputRef, countryCodeFieldClassName, numberFieldClassName, countryCodePlaceholder, placeholder, countryCodeLabel, label, labelDescription, numberLabel, labelSrOnly, numberMask, countries: ccFilter = 'Prioritized', emptyValue, info, warning, size, error, hasError, disabled, width, help, required, validateInitially, validateContinuously, validateUnchanged, omitCountryCodeField, setHasFocus, handleChange, setDisplayValue, onCountryCodeChange, onNumberChange, filterCountries } = (0, _index4.useFieldProps)(preparedProps, { executeOnChangeRegardlessOfUnchangedValue: true }); (0, _react.useEffect)(() => { if (path || itemPath) { var _inputRef$current; const number = (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.value; setDisplayValue((number === null || number === void 0 ? void 0 : number.length) > 0 ? `${countryCodeRef.current} ${number}` : undefined); } }, [inputRef, itemPath, path, setDisplayValue, value]); const filter = (0, _react.useCallback)(country => { return (0, _index5.countryFilter)(country, filterCountries, ccFilter); }, [ccFilter, filterCountries]); const { countries } = (0, _useCountries.default)(); const updateCurrentDataSet = (0, _react.useCallback)(() => { dataRef.current = (0, _index5.getCountryData)({ countries, lang, filter: ccFilter === 'Prioritized' && !wasFilled.current ? country => `${formatCountryCode(country.cdc)}` === countryCodeRef.current : filter, sort: ccFilter, makeObject }); }, [countries, lang, ccFilter, filter]); const prepareEventValues = (0, _react.useCallback)(({ countryCode = countryCodeRef.current || emptyValue, phoneNumber = numberRef.current || emptyValue } = {}) => { var _currentCountryRef$cu2; if (!currentCountryRef.current) { const cdcVal = countryCode === null || countryCode === void 0 ? void 0 : countryCode.replace(/^\+/, '').replace(/-/g, ''); const item = dataRef.current.find(item => { var _item$country; const cdc = item === null || item === void 0 || (_item$country = item.country) === null || _item$country === void 0 || (_item$country = _item$country.cdc) === null || _item$country === void 0 ? void 0 : _item$country.replace(/-/g, ''); return cdc === cdcVal; }); currentCountryRef.current = item === null || item === void 0 ? void 0 : item.country; } return { ...(!omitCountryCodeField ? { countryCode } : {}), phoneNumber, iso: (_currentCountryRef$cu2 = currentCountryRef.current) === null || _currentCountryRef$cu2 === void 0 ? void 0 : _currentCountryRef$cu2.iso }; }, [emptyValue, omitCountryCodeField]); const callOnChange = (0, _react.useCallback)(data => { const eventValues = prepareEventValues(data); handleChange(toEvent(toE164([eventValues.countryCode, eventValues.phoneNumber])), eventValues); }, [prepareEventValues, handleChange]); const callOnBlurOrFocus = (0, _react.useCallback)(hasFocus => { const eventValues = prepareEventValues(); setHasFocus(hasFocus, undefined, eventValues); }, [prepareEventValues, setHasFocus]); (0, _react.useMemo)(() => { const [countryCode, phoneNumber] = splitValue(props.value || value); numberRef.current = phoneNumber; if (lang !== langRef.current || !wasFilled.current) { if (!countryCodeRef.current || countryCode) { countryCodeRef.current = countryCode || defaultCountryCode; } langRef.current = lang; updateCurrentDataSet(); } }, [value, props.value, lang, updateCurrentDataSet]); const handleCountryCodeChange = (0, _react.useCallback)(event => { var _dataObj$selectedKey, _countryCodeRef$curre, _numberRef$current; const data = event.data; const dataObj = data && typeof data === 'object' && 'selectedKey' in data ? data : undefined; const countryCode = countryCodeRef.current = (dataObj === null || dataObj === void 0 || (_dataObj$selectedKey = dataObj.selectedKey) === null || _dataObj$selectedKey === void 0 ? void 0 : _dataObj$selectedKey.trim()) || emptyValue; currentCountryRef.current = dataObj === null || dataObj === void 0 ? void 0 : dataObj.country; if (!numberMask && (_countryCodeRef$curre = countryCodeRef.current) !== null && _countryCodeRef$curre !== void 0 && _countryCodeRef$curre.includes(defaultCountryCode) && ((_numberRef$current = numberRef.current) === null || _numberRef$current === void 0 ? void 0 : _numberRef$current.length) > 8) { const truncatedNumber = numberRef.current.substring(0, 8); callOnChange({ countryCode, phoneNumber: truncatedNumber }); onNumberChange === null || onNumberChange === void 0 || onNumberChange(truncatedNumber); } else { callOnChange({ countryCode }); } onCountryCodeChange === null || onCountryCodeChange === void 0 || onCountryCodeChange(countryCode); }, [emptyValue, numberMask, onCountryCodeChange, callOnChange, onNumberChange]); const handleNumberChange = (0, _react.useCallback)(value => { const phoneNumber = numberRef.current = value || emptyValue; callOnChange({ phoneNumber }); onNumberChange === null || onNumberChange === void 0 || onNumberChange(phoneNumber); }, [emptyValue, callOnChange, onNumberChange]); const handleOnBlur = (0, _react.useCallback)(() => { callOnBlurOrFocus(false); }, [callOnBlurOrFocus]); const handleOnFocus = (0, _react.useCallback)(() => { callOnBlurOrFocus(true); }, [callOnBlurOrFocus]); const handleCountryCodeFocus = (0, _react.useCallback)(({ updateData }) => { if (!wasFilled.current) { wasFilled.current = true; updateCurrentDataSet(); updateData(dataRef.current); } handleOnFocus(); }, [handleOnFocus, updateCurrentDataSet]); const onTypeHandler = (0, _react.useCallback)(({ value, updateData, revalidateInputValue, event }) => { var _event$nativeEvent; if (typeof (event === null || event === void 0 || (_event$nativeEvent = event.nativeEvent) === null || _event$nativeEvent === void 0 ? void 0 : _event$nativeEvent.data) === 'undefined') { const detected = (0, _detectCountryCode.default)(value); const cdcVal = detected ? detected.countryCode.replace(/^\+/, '').replace(/-/g, '') : value; const country = countries.find(({ cdc }) => cdc.replace(/-/g, '') === (cdcVal === null || cdcVal === void 0 ? void 0 : cdcVal.replace(/-/g, ''))); if (country !== null && country !== void 0 && country.cdc) { const countryCode = countryCodeRef.current = formatCountryCode(country.cdc); updateCurrentDataSet(); updateData(dataRef.current); callOnChange({ countryCode }); window.requestAnimationFrame(() => { revalidateInputValue(); }); } } }, [callOnChange, countries, updateCurrentDataSet]); const isDefault = (_countryCodeRef$curre2 = countryCodeRef.current) === null || _countryCodeRef$curre2 === void 0 ? void 0 : _countryCodeRef$curre2.includes(defaultCountryCode); const compositionFieldProps = { id, className: (0, _clsx.default)('dnb-forms-field-phone-number', className), width: 'stretch', label, labelDescription, labelSrOnly, help: undefined, ...(0, _utils.pickSpacingProps)(props) }; return (0, _jsxRuntime.jsxs)(_index3.default, { ...compositionFieldProps, children: [!omitCountryCodeField && (0, _jsxRuntime.jsx)(_index.Autocomplete, { className: (0, _clsx.default)('dnb-forms-field-phone-number__country-code', countryCodeFieldClassName), mode: "async", placeholder: countryCodePlaceholder, labelDirection: "vertical", label: countryCodeLabel === false ? defaultCountryCodeLabel : countryCodeLabel !== null && countryCodeLabel !== void 0 ? countryCodeLabel : defaultCountryCodeLabel, labelSrOnly: countryCodeLabel === false ? true : undefined, data: dataRef.current, value: countryCodeRef.current, status: hasError ? 'error' : undefined, disabled: disabled, onFocus: handleCountryCodeFocus, onBlur: handleOnBlur, onChange: handleCountryCodeChange, onType: onTypeHandler, independentWidth: true, searchNumbers: true, keepSelection: true, selectAll: true, autoComplete: "tel-country-code", noAnimation: props.noAnimation, size: size }), (0, _jsxRuntime.jsx)(_index2.default, { className: (0, _clsx.default)('dnb-forms-field-phone-number__number', numberFieldClassName), type: "tel", autoComplete: "tel-national", emptyValue: emptyValue, layout: "vertical", label: numberLabel === false ? defaultLabel : numberLabel !== null && numberLabel !== void 0 ? numberLabel : defaultLabel, labelSrOnly: numberLabel === false ? true : undefined, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : isDefault ? defaultPlaceholder : undefined, mask: numberMask !== null && numberMask !== void 0 ? numberMask : isDefault ? defaultMask : Array(15).fill(/\d/), onFocus: handleOnFocus, onBlur: handleOnBlur, onChange: handleNumberChange, value: numberRef.current, ref: inputRef, info: info, warning: warning, error: error, disabled: disabled, width: width === 'stretch' ? 'stretch' : props.omitCountryCodeField && width === 'large' ? 'large' : 'medium', help: { ...help, breakout: false, outset: false }, required: required, errorMessages: errorMessages, validateInitially: validateInitially, validateContinuously: validateContinuously, validateUnchanged: validateUnchanged, inputMode: "tel", size: size })] }); } function makeObject(country, lang) { var _country$i18n$lang; const name = (_country$i18n$lang = country.i18n[lang]) !== null && _country$i18n$lang !== void 0 ? _country$i18n$lang : country.i18n.en; const code = formatCountryCode(country.cdc); return { selectedKey: code, selectedValue: `${country.iso} (${code})`, searchContent: [code, name], content: [name, code], country }; } function formatCountryCode(value) { return `+${value}`; } function splitValue(value) { if (typeof value !== 'string') { return [undefined, '']; } if (value.includes(' ')) { return [undefined, '']; } const detected = (0, _detectCountryCode.default)(value); if (detected) { return [detected.countryCode, detected.phoneNumber]; } if (value.startsWith('+')) { return [value, '']; } return [undefined, value]; } function toE164(array) { return array.filter(Boolean).map(part => part.replace(/-/g, '')).join(''); } (0, _withComponentMarkers.default)(PhoneNumber, { _supportsSpacingProps: undefined }); var _default = exports.default = PhoneNumber; //# sourceMappingURL=PhoneNumber.js.map