UNPKG

@gravityforms/components

Version:

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

302 lines (282 loc) 10.3 kB
import { React, PropTypes, classnames } from '@gravityforms/libraries'; import { spacerClasses, uniqueId } from '@gravityforms/utils'; import { useStateWithDep, ConditionalWrapper } from '@gravityforms/react-utils'; import Button from '../Button'; import Label from '../Label'; import HelpText from '../HelpText'; import Icon from '../Icon'; const { useState, forwardRef } = React; /** * @module Input * @description A multi type input component that supports standard text, email, tel. * * @since 1.1.15 * * @param {object} props Component props. * @param {string} props.borderStyle The border style, one of `default`, `error`, or `correct`. * @param {boolean} props.clearable If input is clearable. * @param {object} props.clearableButtonAttributes Custom attributes for the clearable button. * @param {boolean} props.controlled Whether the input is controlled or not. * @param {object} props.customAttributes Custom attributes for the component. * @param {string|Array|object} props.customClasses Custom classes for the component. * @param {boolean} props.disabled If input is disabled. * @param {object} props.helpTextAttributes Custom attribute for the help text. * @param {string} props.helpTextPosition The position of the help text. * @param {string} props.iconAttributes Custom attributes for the icon. * @param {string} props.id Optional id. Auto generated if not passed. * @param {object} props.labelAttributes Any custom attributes for the label. * @param {string} props.name The name attribute for the input. * @param {Function} props.onBlur On blur function handler. * @param {Function} props.onChange On change function handler. * @param {Function} props.onClear On clear function handler. When clearable true, this function is called when it occurs. * @param {Function} props.onFocus On focus function handler. * @param {string} props.placeholder The optional placeholder attribute for the input. * @param {boolean} props.required Whether the field is required or not. * @param {object} props.requiredLabel Required label properties. * @param {string} props.size The input size: regular (`size-r`), large (`size-l`), or extra large (`size-xl`). * @param {string|number|Array|object} props.spacing The spacing for the component, as a string, number, array, or object. * @param {string} props.textSecurity The text security of the input, one of `circle`, `disc`, `square`, or `none`. * @param {string} props.theme The theme of the input. * @param {string} props.type The input type. * @param {string} props.value The input value. * @param {object} props.wrapperAttributes Custom attributes for the wrapper element. * @param {string|Array|object} props.wrapperClasses Custom classes for the wrapper element. * @param {object} props.wrapperTagName Tag to use for the input wrapper. Defaults to `div`. * @param {object|null} ref Ref to the component. * * @return {JSX.Element} The input component. * * @example * import Input from '@gravityforms/components/react/admin/elements/Input'; * * return ( * <Input * onChange={ () => {} } * placeholder="I am a placeholder" * /> * ); * */ const Input = forwardRef( ( { actionButtonAttributes = { label: '', }, borderStyle = 'default', clearable = false, clearableButtonAttributes = {}, controlled = false, customAttributes = {}, customClasses = [], disabled = false, helpTextAttributes = { size: 'text-xs', weight: 'regular', }, helpTextPosition = 'below', iconAttributes = {}, id = '', labelAttributes = { size: 'text-sm', weight: 'medium', }, name = '', onBlur = () => {}, onChange = () => {}, onClear = () => {}, onKeyDown = () => {}, onFocus = () => {}, placeholder = '', required = false, requiredLabel = { size: 'text-sm', weight: 'medium', }, size = 'size-r', spacing = '', textSecurity = 'none', theme = 'cosmos', type = 'text', value = '', wrapperAttributes = {}, wrapperClasses = [], wrapperTagName = 'div', }, ref ) => { const [ stateInputValue, setStateInputValue ] = useState( value ); const [ controlInputValue, setControlInputValue ] = useStateWithDep( value ); const inputValue = controlled ? controlInputValue : stateInputValue; const inputId = id || uniqueId( 'input' ); const helpTextId = `${ inputId }-help-text`; const withIcon = !! ( iconAttributes.icon || iconAttributes.preset ); const withAction = actionButtonAttributes.label && actionButtonAttributes.label.length > 0; const wrapperProps = { ...wrapperAttributes, className: classnames( { 'gform-input-wrapper': true, [ `gform-input-wrapper--theme-${ theme }` ]: true, 'gform-input-wrapper--input': true, 'gform-input-wrapper--with-action': withAction, 'gform-input-wrapper--clearable': clearable, 'gform-input-wrapper--disabled': disabled, ...spacerClasses( spacing ), 'gform-input-wrapper--required': required, [ `gform-input-wrapper--border-${ borderStyle }` ]: true, 'gform-input-wrapper--with-icon': withIcon, [ `gform-input-wrapper--text-security-${ textSecurity }` ]: type !== 'password' && textSecurity !== 'none', }, wrapperClasses ), ref, }; const inputProps = { ...customAttributes, className: classnames( { 'gform-input': true, 'gform-typography--size-text-sm': 'cosmos' === theme, [ `gform-input--${ size }` ]: true, [ `gform-input--${ type }` ]: true, }, customClasses ), disabled: disabled || labelAttributes?.locked === true, id: inputId, name, onBlur, onKeyDown, onChange: ( e ) => { const { value: newInputValue } = e.target; setStateInputValue( newInputValue ); setControlInputValue( newInputValue ); onChange( newInputValue, e ); }, onFocus, type, value: inputValue, }; if ( placeholder ) { inputProps.placeholder = placeholder; } if ( helpTextAttributes.content ) { inputProps[ 'aria-describedby' ] = helpTextId; } if ( required ) { inputProps.required = true; } const labelProps = { ...labelAttributes, htmlFor: inputId, }; const helpTextProps = { ...helpTextAttributes, id: helpTextId, }; const requiredLabelProps = { customClasses: classnames( [ 'gform-input-help-text--required' ], ), id: helpTextId, ...requiredLabel, }; const iconProps = { ...iconAttributes, customClasses: classnames( [ 'gform-input__icon' ], iconAttributes.customClasses || [], ), }; const clearableButtonProps = { icon: 'circle-close', type: 'unstyled', ...clearableButtonAttributes, customClasses: classnames( [ 'gform-input__clearable-button' ], clearableButtonAttributes.customClasses || [], ), }; const actionButtonProps = { iconPosition: 'leading', type: 'white', ...actionButtonAttributes, customClasses: classnames( [ 'gform-input__action-button' ], actionButtonAttributes.customClasses || [], ), onClick: ( e ) => { if ( actionButtonAttributes.onClick ) { actionButtonAttributes.onClick( e, inputValue ); } }, }; const Container = wrapperTagName; return ( <Container { ...wrapperProps }> <Label { ...labelProps } />{ required && <HelpText { ...requiredLabelProps } /> } { helpTextPosition === 'above' && <HelpText { ...helpTextProps } /> } <ConditionalWrapper condition={ withAction } wrapper={ ( ch ) => <div className="gform-input__action-wrapper">{ ch }{ <Button { ...actionButtonProps } /> }</div> } > <ConditionalWrapper condition={ withIcon } wrapper={ ( ch ) => <div className="gform-input__wrapper">{ ch }</div> } > <input { ...inputProps } /> { withIcon && <Icon { ...iconProps } /> } { clearable && inputValue && ( <Button { ...clearableButtonProps } onClick={ () => { setStateInputValue( '' ); setControlInputValue( '' ); onClear(); } } /> ) } </ConditionalWrapper> </ConditionalWrapper> { helpTextPosition === 'below' && <HelpText { ...helpTextProps } /> } </Container> ); } ); Input.propTypes = { borderStyle: PropTypes.string, clearable: PropTypes.bool, clearableButtonAttributes: PropTypes.object, controlled: PropTypes.bool, customAttributes: PropTypes.object, customClasses: PropTypes.oneOfType( [ PropTypes.string, PropTypes.array, PropTypes.object, ] ), disabled: PropTypes.bool, helpTextAttributes: PropTypes.object, helpTextPosition: PropTypes.string, iconAttributes: PropTypes.object, id: PropTypes.string, labelAttributes: PropTypes.object, name: PropTypes.string, onBlur: PropTypes.func, onChange: PropTypes.func, onClear: PropTypes.func, onKeyDown: PropTypes.func, onFocus: PropTypes.func, placeholder: PropTypes.string, required: PropTypes.bool, requiredLabel: PropTypes.object, size: PropTypes.string, spacing: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number, PropTypes.array, PropTypes.object, ] ), textSecurity: PropTypes.string, theme: PropTypes.string, type: PropTypes.string, value: PropTypes.string, wrapperAttributes: PropTypes.object, wrapperClasses: PropTypes.oneOfType( [ PropTypes.string, PropTypes.array, PropTypes.object, ] ), wrapperTagName: PropTypes.string, }; Input.displayName = 'Input'; export default Input;