import React, { useState, useRef, useEffect } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { DISPLAY, Size, AlignItems, BorderRadius, BackgroundColor, } from '../../../helpers/constants/design-system'; import Box from '../../ui/box'; import { Input } from '../input'; import { TEXT_FIELD_SIZES, TEXT_FIELD_TYPES } from './text-field.constants'; export const TextField = ({ autoComplete, autoFocus, className, defaultValue, disabled, error, id, inputProps, inputRef, startAccessory, endAccessory, maxLength, name, onBlur, onChange, onClick, onFocus, placeholder, readOnly, required, size = Size.MD, testId, type = 'text', truncate = true, value, InputComponent = Input, ...props }) => { const internalInputRef = useRef(null); const [focused, setFocused] = useState(false); useEffect(() => { // The blur won't fire when the disabled state is set on a focused input. // We need to set the focused state manually. if (disabled) { setFocused(false); } }, [disabled]); const handleClick = (event) => { const { current } = internalInputRef; if (current) { current.focus(); setFocused(true); } if (onClick && !disabled) { onClick(event); } }; const handleFocus = (event) => { setFocused(true); onFocus && onFocus(event); }; const handleBlur = (event) => { setFocused(false); onBlur && onBlur(event); }; const handleInputRef = (ref) => { internalInputRef.current = ref; if (inputRef && inputRef.current !== undefined) { inputRef.current = ref; } else if (typeof inputRef === 'function') { inputRef(ref); } }; return ( {startAccessory} {endAccessory} ); }; TextField.propTypes = { /** * Autocomplete allows the browser to predict the value based on earlier typed values */ autoComplete: PropTypes.bool, /** * If `true`, the input will be focused during the first mount. */ autoFocus: PropTypes.bool, /** * An additional className to apply to the text-field */ className: PropTypes.string, /** * The default input value, useful when not controlling the component. */ defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** * If `true`, the input will be disabled. */ disabled: PropTypes.bool, /** * If `true`, the input will indicate an error */ error: PropTypes.bool, /** * The id of the `input` element. */ id: PropTypes.string, /** * The the component that is rendered as the input * Defaults to the Text component */ InputComponent: PropTypes.elementType, /** * Attributes applied to the `input` element. */ inputProps: PropTypes.object, /** * Component to appear on the left side of the input */ startAccessory: PropTypes.node, /** * Component to appear on the right side of the input */ endAccessory: PropTypes.node, /** * Use inputRef to pass a ref to the html input element. */ inputRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), /** * Max number of characters to allow */ maxLength: PropTypes.number, /** * Name attribute of the `input` element. */ name: PropTypes.string, /** * Callback fired on blur */ onBlur: PropTypes.func, /** * Callback fired when the value is changed. */ onChange: PropTypes.func, /** * Callback fired when the TextField is clicked on */ onClick: PropTypes.func, /** * Callback fired on focus */ onFocus: PropTypes.func, /** * The short hint displayed in the input before the user enters a value. */ placeholder: PropTypes.string, /** * It prevents the user from changing the value of the field (not from interacting with the field). */ readOnly: PropTypes.bool, /** * If `true`, the input will be required. Currently no visual difference is shown. */ required: PropTypes.bool, /** * The size of the text field. Changes the height of the component * Accepts SM(32px), MD(40px), LG(48px) */ size: PropTypes.oneOf(Object.values(TEXT_FIELD_SIZES)), /** * Type of the input element. Can be TEXT_FIELD_TYPES.TEXT, TEXT_FIELD_TYPES.PASSWORD, TEXT_FIELD_TYPES.NUMBER * Defaults to TEXT_FIELD_TYPES.TEXT ('text') */ type: PropTypes.oneOf(Object.values(TEXT_FIELD_TYPES)), /** * If true will ellipse the text of the input * Defaults to true */ truncate: PropTypes.bool, /** * The input value, required for a controlled component. */ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** * Data test ID for the InputComponent component */ testId: PropTypes.string, /** * TextField accepts all the props from Box */ ...Box.propTypes, }; TextField.displayName = 'TextField';