import * as React from 'react' ; import debounce , { DebouncedFunction } from '../debounce' ; import { componentDefaultProps } from './component-common' ; import ErrorMessages from './error-messages' ; import Help from './help' ; import Row from './row' ; import TextareaControl , { TextareaControlProps } from './controls/textarea' ; const defaultProps = { ... componentDefaultProps , value : '' , updateOnBlur : true , updateOnChange : true , blurDebounceInterval : 0 , changeDebounceInterval : 500 , cols : 0 , rows : 3 , blurCallback : ( name , value ) : void => { } , } ; type TextareaProps = TextareaControlProps & typeof defaultProps ; interface State { currentValue : string ; incomingPropValue : string ; valueIsChanging : boolean ; } class Textarea extends React . Component < TextareaProps , State > { public static defaultProps = defaultProps ; private changeDebounced : DebouncedFunction ; private blurDebounced : DebouncedFunction ; public constructor ( props ) { super ( props ) ; const { value , onSetValue , changeDebounceInterval , blurDebounceInterval } = props ; this . state = { currentValue : value , incomingPropValue : value , valueIsChanging : false , } ; this . changeDebounced = debounce ( onSetValue , changeDebounceInterval ) ; this . blurDebounced = debounce ( onSetValue , blurDebounceInterval ) ; } public static getDerivedStateFromProps ( props , state ) { const { value : incomingPropValue } = props ; if ( incomingPropValue !== state . incomingPropValue ) { return { valueIsChanging : true , incomingPropValue , } ; } return null ; } public shouldComponentUpdate ( nextProps , nextState ) { const { valueIsChanging , incomingPropValue } = nextState ; if ( valueIsChanging === true ) { this . setState ( { valueIsChanging : false , currentValue : incomingPropValue , } ) ; this . props . onSetValue ( incomingPropValue ) ; return false ; } return true ; } private handleChange = ( event ) : void => { const { updateOnChange , changeCallback , name } = this . props ; const { value } = event . currentTarget ; this . setState ( { currentValue : value } ) ; if ( updateOnChange ) { this . changeDebounced ( value ) ; } changeCallback ( name , value ) ; } ; private handleBlur = ( event ) : void => { const { updateOnBlur , blurCallback , name } = this . props ; const { value } = event . currentTarget ; this . setState ( { currentValue : value } ) ; if ( updateOnBlur ) { this . changeDebounced . cancel ( ) ; this . blurDebounced ( value ) ; } if ( blurCallback ) { blurCallback ( name , value ) ; } } ; public render ( ) { const { ... inputProps } = this . props ; Object . keys ( componentDefaultProps ) . forEach ( ( key ) : void => { delete inputProps [ key ] ; } ) ; const { blurDebounceInterval , changeDebounceInterval , updateOnBlur , updateOnChange , blurCallback , ... filteredInputProps } = inputProps ; const { currentValue } = this . state ; const { className , elementRef , errorMessages , help , id , layout , name , required , showErrors , } = this . props ; const markAsInvalid = showErrors && ( errorMessages . length > 0 || required ) ; const element = ( < TextareaControl { ... filteredInputProps } className = { markAsInvalid ? `is-invalid ${ className }` : className } id = { id } value = { currentValue } name = { name } onChange = { this . handleChange } onBlur = { this . handleBlur } elementRef = { elementRef } / > ) ; if ( layout === 'elementOnly' ) { return element ; } return ( < Row { ... this . props } htmlFor = { id } > < > { element } { help ? < Help help = { help } /> : null} { showErrors ? < ErrorMessages messages = { errorMessages } /> : null} < /> < /Row>); } } export default Textarea ;