@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
327 lines (326 loc) • 13.3 kB
JavaScript
"use client";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React, { useMemo, useContext, useCallback, useEffect, useRef } from 'react';
import { Autocomplete, Flex } from '../../../../components';
import classnames from 'classnames';
import countries from '../../constants/countries';
import StringField from '../String';
import FieldBlock from '../../FieldBlock';
import { useFieldProps } from '../../hooks';
import { pickSpacingProps } from '../../../../components/flex/utils';
import SharedContext from '../../../../shared/Context';
import { countryFilter, getCountryData } from '../SelectCountry';
import useTranslation from '../../hooks/useTranslation';
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$curre, _props$width;
const sharedContext = useContext(SharedContext);
const {
label: defaultLabel,
countryCodeLabel: defaultCountryCodeLabel,
errorRequired
} = useTranslation().PhoneNumber;
const lang = (_sharedContext$locale = sharedContext.locale) === null || _sharedContext$locale === void 0 ? void 0 : _sharedContext$locale.split('-')[0];
const countryCodeRef = useRef(props === null || props === void 0 ? void 0 : props.emptyValue);
const prevCountryCodeRef = useRef(countryCodeRef.current);
const numberRef = useRef(props === null || props === void 0 ? void 0 : props.emptyValue);
const dataRef = useRef(null);
const langRef = useRef(lang);
const wasFilled = useRef(false);
const currentCountryRef = useRef();
const errorMessages = useMemo(() => _objectSpread({
'Field.errorRequired': errorRequired,
'Field.errorPattern': errorRequired
}, props.errorMessages), [errorRequired, props.errorMessages]);
const validateRequired = 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 = useCallback(external => {
const [, phoneNumber] = splitValue(external);
if (!phoneNumber && !props.omitCountryCodeField) {
return countryCodeRef.current;
}
return external;
}, [props.omitCountryCodeField]);
const toEvent = useCallback(value => {
const [, phoneNumber] = splitValue(value);
if (!phoneNumber) {
return props.emptyValue;
}
return value;
}, [props.emptyValue]);
const schema = useMemo(() => {
var _props$schema;
return (_props$schema = props.schema) !== null && _props$schema !== void 0 ? _props$schema : {
type: 'string',
pattern: props.pattern
};
}, [props.schema, props.pattern]);
const defaultProps = {
schema,
errorMessages
};
const ref = useRef();
const preparedProps = _objectSpread(_objectSpread(_objectSpread({}, props), defaultProps), {}, {
validateRequired,
fromExternal,
toEvent,
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 = defaultLabel,
numberMask,
countries: ccFilter = 'Prioritized',
emptyValue,
info,
warning,
size,
error,
hasError,
disabled,
width = 'large',
help,
required,
validateInitially,
continuousValidation,
validateContinuously,
validateUnchanged,
omitCountryCodeField,
setHasFocus,
handleChange,
setDisplayValue,
onCountryCodeChange,
onNumberChange,
filterCountries
} = useFieldProps(preparedProps);
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 ? joinValue([countryCodeRef.current, number]) : undefined);
}
}, [inputRef, itemPath, path, setDisplayValue, value]);
const filter = useCallback(country => {
return countryFilter(country, filterCountries, ccFilter);
}, [ccFilter, filterCountries]);
const updateCurrentDataSet = useCallback(() => {
dataRef.current = getCountryData({
lang,
filter: ccFilter === 'Prioritized' && !wasFilled.current ? country => `${formatCountryCode(country.cdc)}` === countryCodeRef.current : filter,
sort: ccFilter,
makeObject
});
}, [lang, filter, ccFilter]);
const prepareEventValues = useCallback(({
countryCode = countryCodeRef.current || emptyValue,
phoneNumber = numberRef.current || emptyValue
} = {}) => {
var _currentCountryRef$cu;
if (!currentCountryRef.current) {
const cdcVal = countryCode === null || countryCode === void 0 ? void 0 : countryCode.replace(/^\+/, '');
const item = dataRef.current.find(item => {
var _item$country;
const cdc = item === null || item === void 0 ? void 0 : (_item$country = item.country) === null || _item$country === void 0 ? void 0 : _item$country.cdc;
return cdc === cdcVal;
});
currentCountryRef.current = item === null || item === void 0 ? void 0 : item.country;
}
return _objectSpread(_objectSpread({}, !omitCountryCodeField ? {
countryCode
} : {}), {}, {
phoneNumber,
iso: (_currentCountryRef$cu = currentCountryRef.current) === null || _currentCountryRef$cu === void 0 ? void 0 : _currentCountryRef$cu.iso
});
}, [emptyValue, omitCountryCodeField]);
const callOnChange = useCallback(data => {
const eventValues = prepareEventValues(data);
handleChange(joinValue([eventValues.countryCode, eventValues.phoneNumber]), eventValues);
}, [prepareEventValues, handleChange]);
const callOnBlurOrFocus = useCallback(hasFocus => {
setHasFocus(hasFocus, undefined, prepareEventValues());
}, [prepareEventValues, setHasFocus]);
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 = useCallback(({
data
}) => {
var _data$selectedKey;
const countryCode = countryCodeRef.current = (data === null || data === void 0 ? void 0 : (_data$selectedKey = data.selectedKey) === null || _data$selectedKey === void 0 ? void 0 : _data$selectedKey.trim()) || emptyValue;
currentCountryRef.current = data === null || data === void 0 ? void 0 : data.country;
callOnChange({
countryCode
});
onCountryCodeChange === null || onCountryCodeChange === void 0 ? void 0 : onCountryCodeChange(countryCode);
}, [emptyValue, callOnChange, onCountryCodeChange]);
const handleNumberChange = useCallback(value => {
const phoneNumber = numberRef.current = value || emptyValue;
callOnChange({
phoneNumber
});
onNumberChange === null || onNumberChange === void 0 ? void 0 : onNumberChange(phoneNumber);
}, [emptyValue, callOnChange, onNumberChange]);
const handleOnBlur = useCallback(() => {
callOnBlurOrFocus(false);
}, [callOnBlurOrFocus]);
const handleOnFocus = useCallback(() => {
callOnBlurOrFocus(true);
}, [callOnBlurOrFocus]);
const handleCountryCodeFocus = useCallback(({
updateData
}) => {
if (!wasFilled.current) {
wasFilled.current = true;
updateCurrentDataSet();
updateData(dataRef.current);
}
handleOnFocus();
}, [handleOnFocus, updateCurrentDataSet]);
const onTypeHandler = useCallback(({
value,
updateData,
revalidateInputValue,
event
}) => {
var _event$nativeEvent;
if (typeof (event === null || event === void 0 ? void 0 : (_event$nativeEvent = event.nativeEvent) === null || _event$nativeEvent === void 0 ? void 0 : _event$nativeEvent.data) === 'undefined') {
const cdcVal = /\+\d{1,3}\s{1}\d+/.test(value) ? splitValue(value)[0] : value;
const country = countries.find(({
cdc
}) => cdc === cdcVal);
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, updateCurrentDataSet]);
const isDefault = (_countryCodeRef$curre = countryCodeRef.current) === null || _countryCodeRef$curre === void 0 ? void 0 : _countryCodeRef$curre.includes(defaultCountryCode);
const fieldBlockProps = _objectSpread({
id,
className: classnames('dnb-forms-field-phone-number', className),
width: omitCountryCodeField || props.width ? undefined : width,
label: undefined,
help: undefined
}, pickSpacingProps(props));
return React.createElement(FieldBlock, fieldBlockProps, React.createElement(Flex.Horizontal, {
align: "flex-end"
}, !omitCountryCodeField && React.createElement(Autocomplete, {
className: classnames('dnb-forms-field-phone-number__country-code', countryCodeFieldClassName),
mode: "async",
placeholder: countryCodePlaceholder,
label_direction: "vertical",
label: countryCodeLabel !== null && countryCodeLabel !== void 0 ? countryCodeLabel : defaultCountryCodeLabel,
data: dataRef.current,
value: countryCodeRef.current,
status: hasError ? 'error' : undefined,
disabled: disabled,
on_focus: handleCountryCodeFocus,
on_blur: handleOnBlur,
on_change: handleCountryCodeChange,
on_type: onTypeHandler,
independent_width: true,
search_numbers: true,
keep_selection: true,
autoComplete: "tel-country-code",
no_animation: props.noAnimation,
stretch: width === 'stretch',
size: size
}), React.createElement(StringField, {
className: classnames('dnb-forms-field-phone-number__number', numberFieldClassName),
type: "tel",
autoComplete: "tel-national",
emptyValue: emptyValue,
layout: "vertical",
label: label,
placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : isDefault ? defaultPlaceholder : undefined,
mask: numberMask !== null && numberMask !== void 0 ? numberMask : isDefault ? defaultMask : Array(12).fill(/\d/),
onFocus: handleOnFocus,
onBlur: handleOnBlur,
onChange: handleNumberChange,
value: numberRef.current,
innerRef: inputRef,
info: info,
warning: warning,
error: error,
disabled: disabled,
width: omitCountryCodeField ? 'medium' : (_props$width = props.width) !== null && _props$width !== void 0 ? _props$width : 'stretch',
help: _objectSpread(_objectSpread({}, help), {}, {
breakout: false,
outset: false
}),
required: required,
errorMessages: errorMessages,
validateInitially: validateInitially,
validateContinuously: continuousValidation || validateContinuously,
validateUnchanged: validateUnchanged,
inputMode: "tel",
size: size
})));
}
function makeObject(country, lang) {
var _country$i18n$lang;
return {
selectedKey: formatCountryCode(country.cdc),
selected_value: `${country.iso} (${formatCountryCode(country.cdc)})`,
content: `${formatCountryCode(country.cdc)} ${(_country$i18n$lang = country.i18n[lang]) !== null && _country$i18n$lang !== void 0 ? _country$i18n$lang : country.i18n.en}`,
country
};
}
function formatCountryCode(value) {
return `+${value}`;
}
function splitValue(value) {
return (typeof value === 'string' ? value.match(/^(\+[^ ]+)? ?(.*)$/) : [undefined, '', '']).slice(1);
}
function joinValue(array) {
return array.filter(Boolean).join(' ');
}
PhoneNumber._supportsSpacingProps = true;
export default PhoneNumber;
//# sourceMappingURL=PhoneNumber.js.map