import * as React from 'react' import './LoginField.css' enum FieldError { REQUIRED = 'Required', INVALID = 'Invalid Value' } type LoginFieldProps = { password: boolean } type LoginFieldSettings = { errorText: FieldError, hasError: boolean, shake: boolean, value: string } export default class LoginField extends React.Component { private readonly USERNAME_REGEX = /^[a-zA-Z0-9_]{1,16}$/ private readonly BASIC_EMAIL_REGEX = /^\S+@\S+\.\S+$/ // private readonly VALID_EMAIL_REGEX = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i private readonly SHAKE_CLASS = 'shake' private errorSpanRef: React.RefObject constructor(props: LoginFieldProps) { super(props) this.state = { errorText: FieldError.REQUIRED, hasError: true, shake: false, value: '' } this.errorSpanRef = React.createRef() } componentDidUpdate() { if(this.state.hasError) { // @ts-ignore Opacity is a number, not a string.. this.errorSpanRef.current!.style.opacity = 1 if(this.state.shake) { this.errorSpanRef.current!.classList.remove(this.SHAKE_CLASS) void this.errorSpanRef.current!.offsetWidth this.errorSpanRef.current!.classList.add(this.SHAKE_CLASS) } } else { // @ts-ignore Opacity is a number, not a string.. this.errorSpanRef.current!.style.opacity = 0 } } private getFieldSvg(): JSX.Element { if(this.props.password) { return ( <> ) } else { return ( <> ) } } private formatError(error: FieldError): string { return `* ${error}` } private getErrorState(shake: boolean, errorText: FieldError): Partial { return { shake, errorText, hasError: true, } } private getValidState(): Partial { return { hasError: false } } private validateEmail = (value: string, shakeOnError: boolean): void => { let newState if(value) { if(!this.BASIC_EMAIL_REGEX.test(value) && !this.USERNAME_REGEX.test(value)) { newState = this.getErrorState(shakeOnError, FieldError.INVALID) } else { newState = this.getValidState() } } else { newState = this.getErrorState(shakeOnError, FieldError.REQUIRED) } this.setState({ ...this.state, ...newState, value }) } private validatePassword = (value: string, shakeOnError: boolean): void => { let newState if(value) { newState = this.getValidState() } else { newState = this.getErrorState(shakeOnError, FieldError.REQUIRED) } this.setState({ ...this.state, ...newState, value }) } private getValidateFunction(): (value: string, shakeOnError: boolean) => void { return this.props.password ? this.validatePassword : this.validateEmail } private handleBlur = (event: React.FocusEvent): void => { this.getValidateFunction()(event.target.value, true) } private handleInput = (event: React.FormEvent): void => { this.getValidateFunction()((event.target as HTMLInputElement).value, false) } render() { return ( <>
{this.getFieldSvg()} {this.formatError(this.state.errorText)}
) } }