UNPKG

@shopgate/engage

Version:
243 lines (239 loc) • 7.73 kB
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;