import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'; import { ErrorMessage, InputElement, InputWrap, Tips, Info, TextAreaElement, SIcon } from './style'; import useReducer from 'utils/useReducerHelper'; import { useSpring } from 'react-spring'; import { IInput } from './types'; const Input = function(props: IInput, ref: React.RefObject) { const { value, onChange, validator, preValidator, tips, clear, size, textarea, style, className, onEnter, icon, iconPosition, iconClick, iconColor = '#b6bbc7', ...rest } = props; const [state, dispatch] = useReducer({ value, errorMessage: '', validateResult: true, // if true no error, if is string: has error and its a errorMsg }); const renderValue = 'value' in props ? value : state.value || ''; const firstRun = useRef(true); useEffect(() => { // skip first run validate check if (validator && !firstRun.current) { const validateResult = validator(renderValue); const state: any = { validateResult }; if (validateResult !== true) { // copy errMsg from validateResult for animation purpose state.errorMessage = validateResult; } dispatch(state); } firstRun.current = false; }, [validator, renderValue]); const inputRef: any = useRef(); const valid = state.validateResult === true; useImperativeHandle(ref, () => ({ isValid: valid, input: inputRef.current })); const errorStyle = useSpring({ height: valid ? 0 : 20, opacity: valid ? 0 : 1, transform: `translate3d(0, -${valid ? 150 : 0}%, 0)`, }); const RenderedElement = textarea ? TextAreaElement : InputElement; const showClose = !textarea && clear && renderValue && state.validateResult === true; return ( { if (e.keyCode === 13) { onEnter && onEnter(e.target.value); } const onKeyDown = (rest as any).onKeyDown; onKeyDown && onKeyDown(e); }} style={{ paddingLeft: icon && iconPosition === 'start' ? 30 : undefined }} ref={inputRef} /> {tips && state.validateResult === true && {tips}} {state.errorMessage} {!textarea && !valid && } {!showClose && icon && ( )} {showClose && ( ) => { (inputRef.current as HTMLInputElement).focus(); dispatch({ value: '' }); if (onChange) { e.target = inputRef.current as HTMLInputElement; e.target.value = ''; onChange(e); } }} /> )} ); function changeHandler(e: React.ChangeEvent) { const preCheck = preValidator ? preValidator(e.target.value) : true; typeof preCheck === 'string' && dispatch({ errorMessage: preCheck }); preValidator && dispatch({ validateResult: preCheck }); if (preCheck !== true) { return; } if (!('value' in props)) { dispatch({ value: e.target.value }); } if (onChange) { onChange(e); } } function blurHandler(e: React.ChangeEvent) { const preCheck = preValidator ? preValidator(e.target.value) : true; typeof preCheck === 'string' && dispatch({ errorMessage: preCheck }); preValidator && dispatch({ validateResult: preCheck }); const onBlur = (rest as any).onBlur; if (onBlur) { onBlur(e); } } }; //@ts-ignore const forwardRefExoticComponent = forwardRef(Input); forwardRefExoticComponent.defaultProps = { size: 'large', width: '120px', }; export default forwardRefExoticComponent; export const IntegerValues = (value: string) => /^-?\d*$/.test(value) || '只能输入整数'; export const PositiveIntegerValues = (value: string) => /^\d*$/.test(value) || '只能输入正整数'; export const PositiveIntegerWithLimit = (limit: number, msg = `只能输入正整数,且不能大于${limit}`) => ( value: string ) => (/^\d*$/.test(value) && (value === '' || parseInt(value) <= limit)) || msg; export const FloatPointValues = (value: string) => /^-?\d*[.,]?\d*$/.test(value) || '只能输入数字'; export const TwoDecimalAtMost = (value: string) => /^-?\d*[.,]?\d{0,2}$/.test(value) || '只能输入数字,小数位数不能大于2';