// Libraries import React, { CSSProperties, ChangeEvent, KeyboardEvent, forwardRef, RefObject, useState, } from 'react' import classnames from 'classnames' // Components import {Icon} from '../Icon' import {StatusIndicator} from './StatusIndicator' // Styles import styles from './Input.scss' // Utils import {styleReducer} from '../../utils/styleReducer' // Types import { AutoComplete, ComponentSize, ComponentStatus, IconFont, InputType, StandardFunctionProps, } from '../../types' export interface InputProps extends StandardFunctionProps { /** Minimum value for number & range types */ min?: number /** Maximum value for number & range types */ max?: number /** Stepping interval granularity for range type */ step?: number /** Determines whether checkbox is checked */ checked?: boolean /** Function to be called on field value change */ onChange?: (e: ChangeEvent) => void /** Function to be called on focus loss */ onBlur?: (e?: ChangeEvent) => void /** Function to be called on focus gain */ onFocus?: (e?: ChangeEvent) => void /** Function to be called on key press */ onKeyPress?: (e: KeyboardEvent) => void /** Function to be called on key up */ onKeyUp?: (e: KeyboardEvent) => void /** Function to be called on key down */ onKeyDown?: (e: KeyboardEvent) => void /** Icon to be displayed to the left of text */ icon?: IconFont /** Maximum string length for input value */ maxLength?: number /** Keyboard control tab order */ tabIndex?: number /** Input type (text, number, password, email, checkbox) */ type?: InputType /** Input field name attribute */ name?: string /** Input field value to be updated with 'on X' functions */ value?: string | number /** Placeholder text when no value is present */ placeholder?: string /** Allows or disallows browser autocomplete functionality */ autocomplete?: AutoComplete /** Text to be displayed on hover tooltip */ titleText?: string /** Text to be displayed on hover tooltip when radio button is disabled */ disabledTitleText?: string /** Input Component size */ size?: ComponentSize /** Input status state */ status?: ComponentStatus /** Whether or not the input receives autofocus when mounted */ autoFocus?: boolean /** Allows or disallows browser spellcheck functionality */ spellCheck?: boolean /** CSS attributes for the input element */ inputStyle?: CSSProperties /** For use within a form, marks the input as required */ required?: boolean /** Pass in a RegEx matcher for best results */ pattern?: string /** Pass through for container ref */ containerRef?: RefObject } export type InputRef = HTMLInputElement export type InputContainerRef = HTMLDivElement export const Input = forwardRef( ( { id, min, max, step, icon, size = ComponentSize.Small, name = '', type = InputType.Text, style = {width: '100%'}, value = '', status = ComponentStatus.Default, onBlur, testID = 'input-field', pattern, onFocus, checked, onKeyUp, required = false, tabIndex, onChange, titleText = '', className, children, autoFocus = false, maxLength, onKeyDown, spellCheck = false, onKeyPress, inputStyle, placeholder = '', containerRef, autocomplete = AutoComplete.Off, disabledTitleText = 'This input is disabled', }, ref ) => { const [isFocused, setFocus] = useState(autoFocus) const correctStatus = value === value ? status : ComponentStatus.Error const inputClasses = classnames('cf-input', { [`cf-input-${size}`]: size, 'cf-input__focused': isFocused, 'cf-input__has-checkbox': type === InputType.Checkbox, 'cf-input__has-icon': icon, 'cf-input__valid': correctStatus === ComponentStatus.Valid, 'cf-input__error': correctStatus === ComponentStatus.Error, 'cf-input__loading': correctStatus === ComponentStatus.Loading, 'cf-input__disabled': correctStatus === ComponentStatus.Disabled, [`${className}`]: className, }) .split(' ') .reduce((accum, current) => styleReducer(styles, accum, current), '') const handleInputFocus = (e: ChangeEvent): void => { setFocus(true) if (onFocus) { onFocus(e) } } const handleInputBlur = (e: ChangeEvent): void => { setFocus(false) if (onBlur) { onBlur(e) } } const inputCheckboxClasses = classnames('cf-input--checkbox', {checked}) .split(' ') .reduce((accum, current) => styleReducer(styles, accum, current), '') const correctlyTypedValue: string | number = value === value ? value : '' const correctType: string = value === value ? type : 'text' const correctlyTypedMin: string | number | undefined = min === min ? min : '' const correctlyTypedMax: string | number | undefined = max === max ? max : '' const iconElement = icon && ( ) const title = status === ComponentStatus.Disabled ? disabledTitleText : titleText return (
{type !== InputType.Checkbox && ( )} {type === InputType.Checkbox && (
)} {iconElement} {children}
) } ) Input.displayName = 'GiraffeInput'