import * as React from "react" ; import { polyfill } from "react-lifecycles-compat" ; import { DISPLAYNAME_PREFIX } from "../../common/props" ; export interface IAsyncControllableInputProps extends React . DetailedHTMLProps < React . InputHTMLAttributes < HTMLInputElement > , HTMLInputElement > { inputRef ? : React . LegacyRef < HTMLInputElement > ; } type InputValue = IAsyncControllableInputProps [ "value" ] ; export interface IAsyncControllableInputState { default isComposing : boolean ; default value : InputValue ; /**
     * The latest input value, which updates during IME composition. Defaults to props.value.
     */ nextValue : InputValue ; default hasPendingUpdate : boolean ; } /**
 * A stateful wrapper around the low-level <input> component which works around a
 * [React bug](https://github.com/facebook/react/issues/3926). This bug is reproduced when an input
 * receives CompositionEvents (for example, through IME composition) and has its value prop updated
 * asychronously. This might happen if a component chooses to do async validation of a value
 * returned by the input's `onChange` callback.
 *
 * Note: this component does not apply any Blueprint-specific styling.
 */ @ polyfill export class AsyncControllableInput extends React . PureComponent < IAsyncControllableInputProps , IAsyncControllableInputState > { public static displayName = `${ DISPLAYNAME_PREFIX }.AsyncControllableInput` ; public state : IAsyncControllableInputState = { hasPendingUpdate : false , isComposing : false , nextValue : this . props . value , value : this . props . value , } ; public static getDerivedStateFromProps ( nextProps , nextState ) { if ( nextState . isComposing || nextProps . value === undefined ) { return null ; } const userTriggeredUpdate = nextState . nextValue !== nextState . value ; if ( userTriggeredUpdate ) { if ( nextProps . value === nextState . nextValue ) { if ( nextState . hasPendingUpdate ) { return { value : nextProps . value , hasPendingUpdate : false } ; } else { return { value : nextState . nextValue } ; } } else { if ( nextProps . value === nextState . value ) { return { hasPendingUpdate : true } ; } return { value : nextProps . value , nextValue : nextProps . value , hasPendingUpdate : false } ; } } else { return { value : nextProps . value , nextValue : nextProps . value , hasPendingUpdate : false } ; } } public render ( ) { const { isComposing , hasPendingUpdate , value , nextValue } = this . state ; const { inputRef , ... restProps } = this . props ; return ( < input { ... restProps } ref = { inputRef } value = { isComposing || hasPendingUpdate ? nextValue : value } onCompositionStart = { this . handleCompositionStart } onCompositionEnd = { this . handleCompositionEnd } onChange = { this . handleChange } / > ) ; } private handleCompositionStart = ( e ) => { this . setState ( { isComposing : true , nextValue : this . state . value , } ) ; this . props . onCompositionStart ?. ( e ) ; } ; private handleCompositionEnd = ( e ) => { this . setState ( { isComposing : false } ) ; this . props . onCompositionEnd ?. ( e ) ; } ; private handleChange = ( e ) => { const { value } = e . target ; this . setState ( { nextValue : value } ) ; this . props . onChange ?. ( e ) ; } ; }