@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
424 lines (423 loc) • 17.3 kB
JavaScript
"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