@shopgate/engage
Version:
Shopgate's ENGAGE library.
243 lines (239 loc) • 7.73 kB
JavaScript
import _isEqual from "lodash/isEqual";
import _upperCase from "lodash/upperCase";
import _camelCase from "lodash/camelCase";
import * as React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { i18n } from '@shopgate/engage/core/helpers';
import { parsePhoneNumber } from 'react-phone-number-input';
import PhoneInputCountrySelect from 'react-phone-number-input/mobile';
import PhoneInput from 'react-phone-number-input/input-mobile';
import { getCountries } from 'react-phone-number-input/input';
import en from 'react-phone-number-input/locale/en';
import de from 'react-phone-number-input/locale/de';
import es from 'react-phone-number-input/locale/es';
import fr from 'react-phone-number-input/locale/fr';
import pt from 'react-phone-number-input/locale/pt';
import flags from 'react-phone-number-input/flags';
import { useCountriesNames } from '@shopgate/engage/i18n';
import { css } from 'glamor';
import { themeConfig } from '@shopgate/engage';
import Label from '@shopgate/pwa-ui-shared/TextField/components/Label';
import FormHelper from "./FormHelper";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const {
variables,
colors
} = themeConfig;
const styles = {
formField: css({
width: '100%',
marginBottom: '0px !important'
}).toString(),
phoneField: css({
position: 'relative',
width: '100%',
paddingTop: variables.gap.big * 0.75,
paddingBottom: variables.gap.big * 1.25,
' input.PhoneInputInput': {
outline: 'none',
fontSize: '1rem',
lineHeight: '1.1875rem',
borderRadius: 0,
paddingBottom: variables.gap.xsmall * 1.5,
borderBottom: `1px solid ${colors.shade12}`,
'&:focus': {
borderBottom: `2px solid ${colors.primary}`,
paddingBottom: variables.gap.xsmall * 1.5 - 1
}
}
}),
phoneFieldError: css({
' input.PhoneInputInput': {
borderBottom: `2px solid var(--color-state-alert, ${colors.error})`,
paddingBottom: variables.gap.xsmall * 1.5 - 1
}
}).toString(),
phoneFieldErrorText: css({
position: 'absolute',
width: '100%',
bottom: '-10px',
fontSize: '0.75rem',
lineHeight: 0.875,
color: `var(--color-state-alert, ${colors.error})`,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
marginLeft: '38px'
})
};
const builtInCountries = getCountries();
const locales = {
en,
de,
es,
fr,
pt
};
/**
* Renders the reserve form phone input maybe with country selection.
* @param {Object} props The component props.
* @returns {JSX.Element}
*/
const UnwrappedElementPhoneNumber = /*#__PURE__*/React.memo(props => {
const {
element,
name,
errorText,
value,
visible,
formName
} = props;
const {
label,
handleChange,
disabled = false,
required = false,
config: {
supportedCountries = [],
countrySortOrder = [],
userLocation = {}
} = {}
} = element;
const [isFocused, setIsFocused] = React.useState(false);
// Maps available countries to correct format.
const countries = React.useMemo(() => {
if (supportedCountries.length === 0) {
return builtInCountries;
}
return supportedCountries.map(country => {
const pieces = country.split('_');
return _upperCase(pieces[0]);
});
}, [supportedCountries]);
const countriesNames = useCountriesNames(countries, locales);
// Get labels for supported countries.
const labels = React.useMemo(() => {
const output = {};
if (!countries) {
return output;
}
countries.forEach(key => {
const pieces = key.split('_');
output[pieces[0]] = countriesNames[key];
});
return output;
}, [countries, countriesNames]);
const defaultCountry = React.useMemo(() => {
let country;
if (value) {
// Try to parse the value to determine a country
const phoneNumber = parsePhoneNumber(value || '');
if (phoneNumber && phoneNumber.country) {
({
country
} = phoneNumber);
}
}
if (!country && userLocation) {
// Take the country from the user location if present
({
country
} = userLocation);
}
if (!country) {
// If no country could be determined yet, take the country from the language
[, country] = i18n.getLang().split('-');
}
// Check if the determined country is included inside the available countries
if (!countries.includes(country)) {
if (countrySortOrder?.length && countries.includes(countrySortOrder[0])) {
// Take first country if the sort order list if present
[country] = countrySortOrder;
} else {
// Take first country from the list
[country] = countries;
}
}
return country;
}, [countries, countrySortOrder, userLocation, value]);
const countryOptionsOrder = React.useMemo(() => {
/**
* To avoid component errors remove countries from the sort order array that are not part
* of the counties array.
*/
const sanitizedCountrySortOrder = countrySortOrder.filter(countryCode => countries.includes(countryCode));
const countryListsEqual = _isEqual([].concat(countries).sort(), [].concat(sanitizedCountrySortOrder).sort());
/**
* When list with supported countries has the same entries as the country sort order, we don't
* need to add a separator to the countryOptionsOrder array since the country picker lists
* will not show a section with unordered countries.
*/
return sanitizedCountrySortOrder.length ? [].concat(sanitizedCountrySortOrder, countryListsEqual ? [] : ['|']) : [];
}, [countries, countrySortOrder]);
const hasCountrySelect = React.useMemo(() => countries.length > 1, [countries.length]);
const handleChangeWrapped = React.useCallback(phoneValue => {
handleChange(phoneValue, {
target: {
name
}
});
}, [handleChange, name]);
const phoneClasses = classnames('textField', {
simpleInput: !hasCountrySelect,
[_camelCase(name)]: true,
phonePicker: true,
phonePickerError: !!errorText,
validationError: !!errorText,
phonePickerFocused: isFocused,
[styles.phoneField]: true,
[styles.phoneFieldError]: !!errorText
});
if (!visible) {
return null;
}
const Component = hasCountrySelect ? PhoneInputCountrySelect : PhoneInput;
return /*#__PURE__*/_jsxs("div", {
className: classnames('formBuilderField', 'engage__form-phone-number', {
validationError: !!errorText
}),
children: [/*#__PURE__*/_jsxs("div", {
className: phoneClasses,
children: [/*#__PURE__*/_jsx(Label, {
label: label,
isFocused: isFocused,
isFloating: isFocused || !!value
}), /*#__PURE__*/_jsx(Component, {
defaultCountry: defaultCountry,
name: name,
value: value || '',
onChange: handleChangeWrapped,
onFocus: () => setIsFocused(true),
onBlur: () => setIsFocused(false),
disabled: disabled,
required: required,
"aria-invalid": !!errorText,
"aria-describedby": errorText.length > 0 ? `ariaError-${name}` : null,
...(hasCountrySelect ? {
countryOptionsOrder,
addInternationalOption: false,
flags,
countries,
labels
} : {
className: 'PhoneInputInput'
})
})]
}), /*#__PURE__*/_jsx(FormHelper, {
errorText: errorText,
element: element,
formName: formName,
elementName: name
})]
});
});
UnwrappedElementPhoneNumber.defaultProps = {
value: '',
visible: true
};
export default UnwrappedElementPhoneNumber;