import { ChangeEvent, Component, createRef } from "react"; import { FormContext, IFormContext } from ".."; // elements import Validators, { IValidationTypes } from "../Validators/Validators"; export type IError = { message: string; validator: string; value: string | number | readonly string[]; args: any[]; isErrored?: (hasError: boolean) => void; }; export type INewBasefieldProps = { onChange?: (event: ChangeEvent) => void; name: string; regex?: RegExp; onCancel?: () => void; disableValidation?: boolean; onErrors?: (errors: IError[]) => void; fieldRef?: React.RefObject; }; export type IProps = IValidationTypes & React.InputHTMLAttributes & INewBasefieldProps; type IState = { value?: string | number | readonly string[]; errors: IError[]; }; export default abstract class BaseField

extends Component { public static override contextType = FormContext; public override context: IFormContext | null = null; public fieldRef: React.RefObject = createRef(); static defaultProps: Partial = { disableValidation: false, }; constructor(props: P) { super(props); this.onChange = this.onChange.bind(this); this.validate = this.validate.bind(this); this.state = { value: this.props.value ?? this.props.defaultValue ?? "", errors: [], }; } public override componentDidMount() { this.context?.setField(this.props.name, this); } public override componentDidUpdate(prevProps: P) { if (prevProps.value !== this.props.value || prevProps.defaultValue !== this.props.defaultValue) { this.setState({ value: this.props.value ?? this.props.defaultValue ?? "" }); } } public override componentWillUnmount() { this.context?.unSetField(this.props.name); } public async onBlur(event: React.FocusEvent) { // this.validate(); // if (this.props.onBlur) { // this.props.onBlur(event); // } } public async validate(isOnSubmit?: boolean) { if (this.props.disableValidation) return; if (this.props.readOnly) return; const errorArray: IError[] = []; const props: { [key: string]: any } = this.props; const validators = Object.entries(Validators).filter(([key]) => props[key]); const isValidable = isOnSubmit ? this.props.required || (this.state.value && this.state.value !== "") : this.state.value && this.state.value !== ""; if (isValidable) { const validations = await Promise.all( validators.map(async ([key, validator]) => { const validation = await (validator.validate as any)(this.state.value, ...(props[key].args ?? [])); if (props[key].isErrored) { props[key].isErrored(!validation); } return [key, validator, validation]; }), ); const unValidateds = validations.filter(([key, validator, validation]) => !validation); const errors: IError[] = unValidateds.map(([key, unValidated]) => { let message = unValidated.message; if (typeof props[key] === "object" && props[key].message) message = props[key].message; return { message, validator: key, value: this.state.value!, args: props[key].args ?? [] }; }); errorArray.push(...errors); } else { validators.forEach(async ([key]) => { if (props[key].isErrored) { props[key].isErrored(false); } }); } this.setState({ errors: errorArray }); this.onErrors(errorArray); return errorArray; } public setErrors(errors: IError[]) { this.setState({ ...this.state, errors }); } /** * It is automatically called by the parent form when the user cancelled the * form and all of its changes. * * Override the method for custom cancelling logic, or pass a custom onCancel * callback. */ public cancel() { if (this.props.onCancel) { this.props.onCancel(); } } public onErrors(errors: IError[]) { if (this.props.onErrors) { this.props.onErrors(errors); } } protected onChange(event: ChangeEvent) { if (this.props.regex) { if (!this.props.regex.test(event.currentTarget.value)) { event.currentTarget.value = event.currentTarget.value.substring(0, event.currentTarget.value.length - 1); } } this.setState({ value: event.currentTarget.value }, () => { this.validate(); this.context?.onFieldChange(this.props.name, this); }); if (this.props.onChange) { this.props.onChange(event); } } }