import React, { HTMLProps } from 'react'; import classnames from 'classnames'; import './Input.scss'; interface OwnProps extends HTMLProps { isValid?: boolean; showInvalidBeforeBlur?: boolean; showInvalidWithoutValue?: boolean; showValidAsPlain?: boolean; setInnerRef?(ref: HTMLInputElement | null): void; } interface State { hasBlurred: boolean; /** * @description when the input has not had any values inputted yet * e.g. "Pristine" condition */ isStateless: boolean; } type Props = OwnProps & HTMLProps; class Input extends React.Component { public state: State = { hasBlurred: false, isStateless: true }; public render() { const { setInnerRef, showInvalidBeforeBlur, showInvalidWithoutValue, showValidAsPlain, isValid, ...htmlProps } = this.props; const { hasBlurred, isStateless } = this.state; const hasValue = !!this.props.value && this.props.value.toString().length > 0; // Currently we don't ever highlight valid, so go empty string instead const validDueToValue = !hasValue && !showInvalidWithoutValue; const validDueToBlur = !hasBlurred && !showInvalidBeforeBlur; let validClass = isValid ? '' : 'invalid'; // Override validity based on initial conditions. if (isStateless || validDueToValue || validDueToBlur) { validClass = ''; } // Show an error with no value only after blurring. if (!hasValue && showInvalidWithoutValue && !validDueToBlur) { validClass = 'invalid'; } const classname = classnames('input-group-input', this.props.className, validClass); return ( setInnerRef && setInnerRef(node)} onBlur={e => { this.setState({ hasBlurred: true }); if (this.props && this.props.onBlur) { this.props.onBlur(e); } }} onChange={this.handleOnChange} onWheel={this.props.type === 'number' ? this.preventNumberScroll : undefined} className={classname} /> ); } private handleOnChange = (args: React.FormEvent) => { if (this.state.isStateless) { this.setState({ isStateless: false }); } if (this.props.onChange) { this.props.onChange(args); } }; // When number inputs are scrolled on while in focus, the number changes. So we blur // it if it's focused to prevent that behavior, without preventing the scroll. private preventNumberScroll(ev: React.WheelEvent) { if (document.activeElement === ev.currentTarget) { ev.currentTarget.blur(); } } } export default Input;