import React, { useState, useRef, useEffect } from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { DISPLAY, SIZES, ALIGN_ITEMS, TEXT, COLORS, } from '../../../helpers/constants/design-system'; import Box from '../../ui/box'; import { Text } from '../text'; import { TEXT_FIELD_BASE_SIZES, TEXT_FIELD_BASE_TYPES, } from './text-field-base.constants'; export const TextFieldBase = ({ autoComplete, autoFocus, className, defaultValue, disabled, error, id, inputProps, inputRef, leftAccessory, rightAccessory, maxLength, name, onBlur, onChange, onClick, onFocus, placeholder, readOnly, required, size = SIZES.MD, type = 'text', truncate = true, value, InputComponent = Text, ...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) { 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 ( {leftAccessory} {rightAccessory} ); }; TextFieldBase.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-base */ 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 */ leftAccessory: PropTypes.node, /** * Component to appear on the right side of the input */ rightAccessory: 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_BASE_SIZES)), /** * Type of the input element. Can be TEXT_FIELD_BASE_TYPES.TEXT, TEXT_FIELD_BASE_TYPES.PASSWORD, TEXT_FIELD_BASE_TYPES.NUMBER * Defaults to TEXT_FIELD_BASE_TYPES.TEXT ('text') */ type: PropTypes.oneOf(Object.values(TEXT_FIELD_BASE_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]), /** * TextFieldBase accepts all the props from Box */ ...Box.propTypes, }; TextFieldBase.displayName = 'TextFieldBase';