UNPKG

@gravityforms/components

Version:

UI components for use in Gravity Forms development. Both React and vanilla js flavors.

201 lines (186 loc) 7.47 kB
import { classnames, React } from '@gravityforms/libraries'; import { usePhoneInputFormatUtilsContext as usePhoneInputFormatUtils } from '@gravityforms/react-utils'; import { spacerClasses } from '@gravityforms/utils'; import Box from '../../elements/Box'; import HelpText from '../../elements/HelpText'; import Input from '../../elements/Input'; import Dropdown from '../Dropdown'; import Loader from '../Loaders/RingLoader'; const { forwardRef, lazy, Suspense } = React; const PhoneComponent = lazy( () => import( './PhoneComponent' ) ); const PhoneComponentPlaceholder = ( { countries, customAttributes = {}, customClasses = [], dropdownAttributes = {}, dropdownClasses = [], inputAttributes = {}, inputClasses = [], label = '', labelAttributes = {}, labelClasses = [], language = 'en', required = false, requiredLabelAttributes = {}, requiredLabelClasses = [], size = 'r', spacing = '', width = 300, } ) => { const disabled = true; const componentProps = { className: classnames( { 'gform-phone': true, 'gform-phone--placeholder': true, 'wp-exclude-emoji': true, [ `gform-phone--size-${ size }` ]: true, 'gform-phone--disabled': disabled, ...spacerClasses( spacing ), }, customClasses ), style: { width: width ? `${ width }px` : undefined, }, ...customAttributes, }; const labelProps = { className: classnames( [ 'gform-phone__label', 'gform-text', 'gform-text--color-port', 'gform-typography--size-text-sm', 'gform-typography--weight-medium', ], labelClasses ), ...labelAttributes, }; const requiredLabelProps = { size: 'text-sm', weight: 'medium', ...requiredLabelAttributes, customClasses: classnames( [ 'gform-phone__required' ], requiredLabelClasses ), }; const dropdownProps = { countries, hasSearch: true, popoverMaxHeight: 300, showCallingCode: true, ...dropdownAttributes, customClasses: classnames( [ 'gform-phone__dropdown', ], dropdownClasses ), disabled, language, searchAttributes: { wrapperClasses: [ 'gform-phone__dropdown-search-wrapper' ], ...( dropdownAttributes?.searchAttributes || {} ), }, searchClasses: [ 'gform-phone__dropdown-search' ], size, triggerClasses: [ 'gform-phone__dropdown-trigger' ], }; const inputProps = { ...inputAttributes, customClasses: classnames( [ 'gform-phone__input', ], inputClasses ), directControlled: true, disabled, required, size: `size-${ size }`, type: 'tel', wrapperClasses: classnames( { 'gform-phone__input-wrapper': true, }, inputAttributes?.wrapperClasses || [] ), }; return ( <div { ...componentProps }> { label && <div { ...labelProps }>{ label }{ required && <HelpText { ...requiredLabelProps } /> }</div> } <div className="gform-phone__wrapper"> <Dropdown { ...dropdownProps } /> <Input { ...inputProps } /> </div> </div> ); }; /** * @module Phone * @description A phone input component with country code selection. * * @since 5.5.0 * * @param {object} props Component props * @param {Array} props.countries Array of country codes. * @param {object} props.customAttributes Custom attributes for the component. * @param {string|Array|object} props.customClasses Custom classes for the component. * @param {boolean} props.disabled Whether the input is disabled. * @param {object} props.dropdownAttributes Custom attributes for the dropdown. * @param {string|Array|object} props.dropdownClasses Custom classes for the dropdown. * @param {object} props.helpTextAttributes Custom attributes for the help text. * @param {string|Array|object} props.helpTextClasses Custom classes for the help text. * @param {object} props.i18n i18n object for the country dropdown component. * @param {string} props.id Optional id (auto-generated if not provided). * @param {string} props.inputAttributes Custom attributes for the input. * @param {string|Array|object} props.inputClasses Custom classes for the input. * @param {boolean} props.international Whether the phone number is international format. * @param {object} props.label Label text. * @param {object} props.labelAttributes Custom attributes for the label. * @param {string|Array|object} props.labelClasses Custom classes for the label. * @param {string} props.language Language code for the country dropdown component. * @param {Function} props.onChange Change handler function, fires both on input and dropdown change. Receives { country, number, isValid }. * @param {Array} props.preferredCountries Array of preferred country codes. * @param {boolean} props.required Whether the field is required. * @param {object} props.requiredLabelAttributes Custom attributes for the required label. * @param {string|Array|object} props.requiredLabelClasses Custom classes for the required label. * @param {string} props.size Component size: 'r', 'l', or 'xl'. * @param {string|number|Array|object} props.spacing Component spacing. * @param {boolean} props.usePlaceholder Whether to use a placeholder. * @param {boolean} props.useValidation Whether to use validation for the phone input. * @param {number} props.width Width of the component in pixels. * @param {object} ref Forwarded ref. * * @return {JSX.Element} Phone component * * @example * import Phone from '@gravityforms/components/react/admin/modules/Phone'; * * return <Phone * countries={ [ 'US', 'CA', 'GB', 'MX', 'DE', 'JP' ] } * helpTextAttributes={ { content: 'The phone number you entered is not valid.' } } * i18n={ { allCountries: 'All countries' } } * id="contact-phone" * international={ true } * label="Phone Number" * onChange={ ( value, event ) => { * console.log( 'country: ', value.country ); * console.log( 'number: ', value.number ); * console.log( 'isValid: ', value.isValid ); * console.log( 'event: ', event ); * } } * preferredCountries={ [ 'US', 'CA', 'GB' ] } * required={ true } * requiredLabelAttributes={ { content: '(Required)' } } * size="xl" * usePlaceholder={ true } * useValidation={ true } * width={ 500 } * />; * */ const Phone = forwardRef( ( props, ref ) => { const { width = 300 } = props; const { isLoading } = usePhoneInputFormatUtils(); const fallback = ( <Box customClasses={ [ 'gform-phone__fallback' ] } x={ width }> <PhoneComponentPlaceholder { ...props } /> <Loader foreground="#9092b2" /> </Box> ); if ( isLoading ) { return fallback; } return ( <Suspense fallback={ fallback }> <PhoneComponent { ...props } ref={ ref } /> </Suspense> ); } ); export default Phone;