& 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);
+ }
+ }
+}
diff --git a/src/front/Components/DesignSystem/Form/Elements/InputField/classes.module.scss b/src/front/Components/DesignSystem/Form/Elements/InputField/classes.module.scss
new file mode 100644
index 00000000..99ef920e
--- /dev/null
+++ b/src/front/Components/DesignSystem/Form/Elements/InputField/classes.module.scss
@@ -0,0 +1,95 @@
+@import "@Themes/constants.scss";
+
+.root {
+ position: relative;
+ textarea{
+ resize: none;
+ height: auto;
+ box-sizing: border-box;
+ font-family: 'Inter';
+ font-style: normal;
+ font-weight: 400;
+ font-size: 18px;
+ line-height: 22px;
+ }
+ .input {
+ z-index: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 24px;
+ gap: 10px;
+ width: 530px;
+ height: 70px;
+ border: 1px solid $grey-medium;
+
+ &:focus {
+ ~ .fake-placeholder {
+ transform: translateY(-35px);
+ transition: transform 0.3s ease-in-out;
+ }
+ }
+ &:not([value=""]) {
+ ~ .fake-placeholder {
+ transform: translateY(-35px);
+ transition: transform;
+ }
+ }
+
+ &[type="number"] {
+ &:focus {
+ ~ .fake-placeholder {
+ transform: translateY(-35px);
+ transition: transform 0.3s ease-in-out;
+ }
+ }
+ &:not([value=""]) {
+ ~ .fake-placeholder {
+ transform: translateY(-35px);
+ transition: transform;
+ }
+ }
+ }
+
+ &:not([value=""]) {
+ ~ .fake-placeholder {
+ transform: translateY(-35px);
+ transition: transform 0.3s ease-in-out;
+ }
+ }
+
+ ~ .fake-placeholder {
+ z-index: 2;
+ top: 35%;
+ margin-left: 8px;
+ padding: 0 16px;
+ pointer-events: none;
+ position: absolute;
+ background: $white;
+ }
+ }
+}
+
+.textarea {
+ z-index: 1;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 24px;
+ gap: 10px;
+
+ width: 530px;
+ height: 70px;
+ border: 1px solid $grey-medium;
+
+ ~ .fake-placeholder {
+ z-index: 2;
+ top: -12px;
+ margin-left: 8px;
+ padding: 0 16px;
+ pointer-events: none;
+ position: absolute;
+ background: $white;
+ // transform: translateY(-35px);
+ }
+}
diff --git a/src/front/Components/DesignSystem/Form/Elements/InputField/index.tsx b/src/front/Components/DesignSystem/Form/Elements/InputField/index.tsx
new file mode 100644
index 00000000..9071adff
--- /dev/null
+++ b/src/front/Components/DesignSystem/Form/Elements/InputField/index.tsx
@@ -0,0 +1,94 @@
+import Typography, { ITypo, ITypoColor } from "@Front/Components/DesignSystem/Typography";
+import { ReactNode } from "react";
+
+import Validators from "../../Validators/Validators";
+import BaseField, { IProps as IBaseFieldProps } from "../BaseField";
+import classes from "./classes.module.scss";
+
+export type IProps = IBaseFieldProps & {
+ fakeplaceholder: string;
+ textarea?: boolean;
+};
+
+// @ts-ignore TODO: typing error on IProps (validator class?? cf Massi 22/02/23)
+export default class InputField extends BaseField {
+ public override render(): ReactNode {
+ let pattern;
+
+ if (this.props.type === "number") {
+ pattern = "(^[0-9]*)(\\.{0,1})([0-9]*)$";
+ }
+
+ if (this.props.pattern) {
+ pattern = this.props.pattern;
+ }
+
+ if (this.props.fieldRef) {
+ this.fieldRef = this.props.fieldRef;
+ }
+
+ // we always need to control the input so we need to set the value as "" by default
+ const value = this.state.value ?? "";
+
+ if (this.props.textarea === true) {
+ return (
+
+
+
+ );
+ } else {
+ return (
+
+
+
0}
+ className={
+ this.props.className ? [classes["input"], classes[this.props.className]].join(" ") : classes["input"]
+ }
+ />
+
{this.props.fakeplaceholder}
+
+
+ );
+ }
+ }
+
+ // We filter the props we'll pass to the primitive input as they're useless for it
+ // It also avoids the console warning because of passing useless props to a primitive DOM element
+ private getHtmlAttributes() {
+ const htmlAttributes = { ...this.props };
+
+ delete htmlAttributes.disableValidation;
+ delete htmlAttributes.onErrors;
+ delete htmlAttributes.fieldRef;
+ delete htmlAttributes.className;
+ delete htmlAttributes.defaultValue;
+
+ for (const validator in Validators) {
+ delete (htmlAttributes as { [key: string]: any })[validator];
+ }
+
+ return htmlAttributes;
+ }
+}
diff --git a/src/front/Components/DesignSystem/Form/InputField/index.tsx b/src/front/Components/DesignSystem/Form/InputField/index.tsx
new file mode 100644
index 00000000..1088d687
--- /dev/null
+++ b/src/front/Components/DesignSystem/Form/InputField/index.tsx
@@ -0,0 +1,77 @@
+import { ReactNode } from "react";
+
+import Validators from "../Validators/Validators";
+//import { IProps as IBaseFieldProps } from "../.";
+import classes from "./classes.module.scss";
+import BaseField, { IProps as IBaseFieldProps } from "../Elements/BaseField";
+
+export type IProps = IBaseFieldProps & {
+ large?: boolean;
+};
+
+export default class InputField extends BaseField {
+ public override render(): ReactNode {
+ let pattern;
+
+ if (this.props.type === "number") {
+ pattern = "(^[0-9]*)(\\.{0,1})([0-9]*)$";
+ }
+
+ if (this.props.pattern) {
+ pattern = this.props.pattern;
+ }
+
+ if (this.props.fieldRef) {
+ this.fieldRef = this.props.fieldRef;
+ }
+
+ // we always need to control the input so we need to set the value as "" by default
+ const value = this.state.value ?? "";
+
+ if (this.props.large === true) {
+ return (
+