2023-05-15 18:36:56 +02:00

121 lines
3.4 KiB
TypeScript

import React, { ReactNode } from "react";
import BaseField, { IProps as IBaseFieldProps } from "./BaseField";
export type IBaseField = BaseField<IBaseFieldProps>;
export type IFormContext = {
setField: (name: string, field: IBaseField) => void;
unSetField: (name: string) => void;
onFieldChange: (name: string, field: IBaseField) => void;
};
type IFields = {
[key: string]: IBaseField;
};
type IState = {};
export type IProps = {
onFieldChange?: (name: string, field: IBaseField) => void;
onSubmit?: (
e: React.FormEvent<HTMLFormElement> | null,
values: { [key: string]: string },
) => void;
className?: string;
children?: ReactNode;
};
export const FormContext = React.createContext<IFormContext>({ setField: () => {}, unSetField: () => {}, onFieldChange: () => {} });
export default class Form extends React.Component<IProps, IState> {
protected fields: IFields = {};
private formRef: React.RefObject<HTMLFormElement>;
constructor(props: IProps) {
super(props);
this.state = {};
this.setField = this.setField.bind(this);
this.unSetField = this.unSetField.bind(this);
this.onFieldChange = this.onFieldChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.formRef = React.createRef();
}
public override render() {
return (
<FormContext.Provider
value={{
setField: this.setField,
unSetField: this.unSetField,
onFieldChange: this.onFieldChange,
}}>
<form className={this.props.className} ref={this.formRef} onSubmit={this.onSubmit}>
{this.props.children}
</form>
</FormContext.Provider>
);
}
public async onSubmit(e: React.FormEvent<HTMLFormElement> | null) {
e?.preventDefault();
const allChildren = this.getAllChildrenFields(e);
const elementsValues = allChildren
.filter((e) => {
return e.getAttribute("type") !== "radio" && e.getAttribute("type") !== "checkbox";
})
.reduce((obj, element) => ({ ...obj, [element.getAttribute("name") ?? ""]: (element as any).value }), {});
const radioInputs = allChildren.filter((e) => e.getAttribute("type") === "radio").filter((e) => (e as any).checked);
const radioInputsValues = radioInputs.reduce(
(obj, element) => ({ ...obj, [element.getAttribute("name") ?? ""]: (element as any).value }),
{},
);
const checkBoxesInput = allChildren.filter((e) => e.getAttribute("type") === "checkbox").filter((e) => (e as any).checked);
const checkBoxesValues = checkBoxesInput.reduce((obj, element) => {
const inputName = element.getAttribute("name") ?? "";
const inputValue = (element as any).value as string;
const newValue = ((obj as any)[inputName] as string[]) ?? [];
newValue.push(inputValue);
return {
...obj,
[inputName]: newValue,
};
}, {});
const allInputs = {
...elementsValues,
...radioInputsValues,
...checkBoxesValues,
};
// Deleting empty input
delete (allInputs as any)[""];
if (this.props.onSubmit) {
this.props.onSubmit(e, allInputs);
}
return { values: elementsValues };
}
protected setField(name: string, field: IBaseField) {
this.fields[name] = field;
}
protected unSetField(name: string) {
delete this.fields[name];
}
protected async onFieldChange(name: string, field: IBaseField) {
if (this.props.onFieldChange) {
this.props.onFieldChange(name, field);
}
}
private getAllChildrenFields(e: React.FormEvent<HTMLFormElement> | null): Element[] {
return Array.from(((e?.target as HTMLFormElement) ?? this.formRef.current).elements);
}
}