# RHF Mui Form

  This repository contains a custom form component built using React Hook Form (RHF) and Material-UI (MUI).

  It allows for form-controlled selection of options via React Hook Form's `Controller` component.

## Table of Contents

- [Features](#features)
- [Example](#example)


## Installation
To install, you can use npm or yarn or pnpm:

  ```js
npm install @paratco/rhf-mui-form

yarn add @paratco/rhf-mui-form

pnpm add @paratco/rhf-mui-form
```

## Features

  ### `RHFTextField`

`RHFTextField` is a wrapper around MIUI's `TextField` component that integrates with React Hook Form.

 ```tsx
 <RHFTextField
   name="firstName"
   label="First Name"
   control={control} // Optional, if useFormContext is not used
   inputDir="ltr"
   isReadOnly={true}
 />
  ```
  | Prop         | Type              | Default    | Definition                                                                                              |
  | ------------ | ---------         | -------    | ------------------------------------------------------------------------------------------------------- |
  | name\*       | `Path<T>`         |            | The name of the input                                                                                   |
  | control      | `Control`         |            | The control object from React Hook Form, optional if useFormContext is used                             |
  | inputDir     | (`ltr` or `rtl`)  |            | If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field |
  | isReadOnly   | boolean           |   False    | The `isReadOnly` prop to make the input read-only                                                       |
  | props        | TextFieldProps    |            | Additional props passed to the underlying MUI `TextField`                                               |

  --------------------------------

  ### `RHFTextMasked`

  `RHFTextMasked` is a React Hook Form integrated `TextField` component that supports input masking via `react-imask`.

  ```tsx
  <RHFTextMasked
    name="phoneNumber"
    label="Phone Number"
    maskOptions={{ mask: "(000) 000-0000" }}
    control={control} // Optional, if useFormContext is not used
    inputDir="ltr"
    isReadOnly={true}
  />
  ```

  | Prop           | Type              | Default    | Definition                                                                                              |
  | ------------   | ---------         | -------    | ------------------------------------------------------------------------------------------------------- |
  | name\*         | `Path<T>`         |            | The name of the input                                                                                   |
  | maskOptions\*  | ReactMaskOpts     |            | The options for input masking, as defined by `react-imask                                              | 
  | control        | `Control`         |            | The control object from React Hook Form, optional if useFormContext is used                             |
  | inputDir       | (`ltr` or `rtl`)  |            | If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field |
  | isReadOnly     | boolean           |   False    | The `isReadOnly` prop to make the input read-only                                                       |
  | props          | TextFieldProps    |            | Additional props passed to the underlying MUI `TextField`                                               |
  --------------------------------

  ### `RHFPasswordField`

  `RHFPasswordField` is a masked password input integrated with React Hook Form. It renders a text field with a show/hide password toggle button using an eye icon.

  - Built on top of `RHFTextMasked`, so it supports input masking via `react-imask`.
  - Input direction is always `ltr` (suitable for passwords).
  - The toggle button shows/hides the password without affecting form state.

  ```tsx
  <RHFPasswordField
    name="password"
    label="Password"
    control={control} // Optional, if useFormContext is not used
    maskOptions={{ mask: /^\S*$/ }} // no spaces
  />
  ```

  | Prop           | Type              | Default | Definition                                                                                |
  | -------------- | ----------------- | ------- | ----------------------------------------------------------------------------------------- |
  | name\*         | `Path<T>`         |         | The name of the input                                                                     |
  | label\*        | `ReactNode`       |         | The label displayed on the input                                                          |
  | maskOptions\*  | `ReactMaskOpts`   |         | The options for input masking, as defined by `react-imask`                                |
  | control        | `Control`         |         | The control object from React Hook Form, optional if useFormContext is used               |

  --------------------------------

  ### `RHFAutoComplete`

  `RHFAutoComplete` is a reusable autocomplete component integrated with React Hook Form.

  - Supports single or multiple option selections based on the `Autocomplete` component.

  ```tsx
  <RHFAutoComplete
    name="category"
    label="Category"
    options={categoryOptions}
    control={control} // Optional, if useFormContext is not used
    multiple // Optional, if you want
  />
  ```
  Types:

  ```ts
  export interface SelectOptionBase {
    label: string;
    value: unknown;
    disabled?: boolean;
  }

  interface OptionItem extends SelectOptionBase {
  value: string;
  }

  ```

  | Prop              | Type                                                              | Default | Definition                                                                    |
  | ----------------- | ----------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------- |
  | name\*            | `Path<T>`                                                         |         | The name of the input                                                         |
  | label\*           | string                                                            |         | The name of the input                                                         |
  | options\*         | OptionItem[ ]                                                     |         | The options to be displayed in the autocomplete dropdown.                     |
  | control           | `Control`                                                         |         | The control object from React Hook Form, optional if useFormContext is used   |
  | renderInputProps  | TextFieldProps                                                    |         | Additional props passed to the underlying MUI `TextField`                     |
  | props             | `AutocompleteProps<Value, Multiple, DisableClearable, FreeSolo>`  |         | Additional props passed to the `Autocomplete` component.                      |
  --------------------------------

  ### `RHFCheckBox`

  `RHFCheckBox` is a wrapper around MIUI's `Checkbox` component that integrates with React Hook Form.

   ```tsx
   <RHFCheckBox
     name="termsAndConditions"
     label="I agree to the terms and conditions"
     control={control} // Optional, if useFormContext is not used
   />
   ```
  | Prop         | Type              | Default    | Definition                                                                            |
  | ------------ | ---------         | -------    | --------------------------------------------------------------------------------------|
  | name\*       | `Path<T>`         |            | The name of the input                                                                 |
  | label\*      | string            |            | The label for the checkbox                                                            |
  | control      | `Control`         |            | The control object from React Hook Form, optional if useFormContext is used           |
  | props        | CheckboxProps     |            | Additional props passed to the underlying MUI `Checkbox`                              |
  --------------------------------

  ### `RHFRadioGroup`

   `RHFRadioGroup` is a wrapper around MIUI's `RadioGroup` component that integrates with React Hook Form.

  - It renders a group of radio buttons based on the provided options and manages the form state.
  - The `formLabel` prop allows for an optional MUI `FormLabel` component to be displayed above the radio buttons.

   ```tsx
    <RHFRadioGroup
      name="gender"
      options={[
        { label: "Male", value: "male" },
        { label: "Female", value: "female" },
      ]}
      formLabel={<FormLabel>Gender</FormLabel>} // Optional FormLabel
      control={control} // Optional, if useFormContext is not used
    />
  ```
  | Prop         | Type                              | Default    | Definition                                                                                              |
  | ------------ | ---------                         | -------    | ------------------------------------------------------------------------------------------------------- |
  | name\*       | `Path<T>`                         |            | The name of the input                                                                                   |
  | options\*    | OptionItem[ ]                     |            | An array of options for the radio buttons, each having a label and value.                               |
  | formLabel    | `ReactElement<typeof FormLabel>`  |            | A FormLabel component to be displayed above the radio buttons                                           |
  | control      | `Control`                         |            | The control object from React Hook Form, optional if useFormContext is used                             |
  | props        | RadioGroupProps                   |            | Additional props passed to the underlying MUI `RadioGroup`                                              |

--------------------------------

  ### `RHFSelect`

   `RHFSelect` is a wrapper around MIUI's `Select` component that integrates with React Hook Form.

   - It supports both single and multiple selections and handles validation and error messages.
   - The `options` prop allows for dynamic option generation, including support for disabling options.

   ```tsx
    <RHFSelect
      name="favoriteFruits"
      options={[
        { label: "Apple", value: "apple" },
        { label: "Banana", value: "banana" },
        { label: "Cherry", value: "cherry", disabled: true },
      ]}
      control={control} // Optional, if useFormContext is not used
      multiple // if you want
    />
  ```

  Types:

  ```ts
    export interface SelectOptionBase {
      label: string;
      value: unknown;
      disabled?: boolean;
    }

    interface OptionItem extends SelectOptionBase {
      /** The value of the option, which is used as the key */
      value: string; // Different between RHFSelect and RHFSelectPro
    }
  ```

  | Prop                | Type              | Default    | Definition                                                                                                |
  | ------------        | ---------         | -------    | -------------------------------------------------------------------------------------------------------   |
  | name\*              | `Path<T>`         |            | The name of the input                                                                                     |
  | options\*           | OptionItem[ ]     |            | An array of option items to be displayed in the select dropdown                                           |
  | inputDir            | (`ltr` or `rtl`)  |            | If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field |
  | control             | `Control`         |            | The control object from React Hook Form, optional if useFormContext is used                               |
  | maxHeight           | number            |            | The maximum height of the select input                                                                    | 
  | dropDownMaxHeight   | number            |            | The maximum height of the dropdown menu                                                                   |
  | props               | SelectProps       |            | Additional props passed to the underlying MUI `Select`                                                    |

--------------------------------

  ### `RHFSlider`

  `RHFSlider` is a wrapper around MUI's `Slider` component that integrates with React Hook Form.

  - Supports both single value and range (two thumbs) sliders based on the type of the form field value (`number` for single, `number[]` for range).
  - Supports RTL/LTR direction override via `sliderDir`.

  ```tsx
  // Single value — one thumb
  const { control } = useForm({ defaultValues: { similarity: 50 } });

  <RHFSlider
    name="similarity"
    label="Similarity"
    control={control}
    min={0}
    max={100}
    step={1}
    sliderDir="ltr"
  />
  ```

  ```tsx
  // Range — two thumbs, because defaultValue is number[]
  const { control } = useForm({ defaultValues: { priceRange: [200, 800] } });

  <RHFSlider
    name="priceRange"
    label="Price Range"
    control={control}
    min={0}
    max={1000}
    sliderDir="ltr"
  />
  ```

  > **The component itself is identical in both cases. Two thumbs vs one thumb is purely determined by the default value type — `number` → one thumb, `number[]` → two thumbs.**

  Types:

  ```ts
  // Single value
  const { control } = useForm({ defaultValues: { similarity: 50 } });

  // Range
  const { control } = useForm({ defaultValues: { priceRange: [200, 800] } });
  ```

  | Prop            | Type                    | Default  | Definition                                                                                              |
  | --------------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------- |
  | name\*          | `Path<T>`               |          | The name of the input                                                                                   |
  | control         | `Control`               |          | The control object from React Hook Form, optional if useFormContext is used                             |
  | label           | `ReactNode`             |          | Optional label displayed above the slider                                                               |
  | sliderDir       | (`ltr` or `rtl`)        |          | Overrides the slider direction
  | helperText      | `ReactNode`             |          | Helper text displayed below the slider                                                                  |
  | hasEmptyHelper  | boolean                 | `true`   | Whether to reserve space for the helper text even when empty, preventing layout shifts                  |
  | props           | `SliderProps`           |          | Additional props passed to the underlying MUI `Slider` (e.g. `min`, `max`, `step`, `marks`, `valueLabelDisplay`) |

--------------------------------

  ### `RHFSelectPro`

  `RHFSelectPro` is a wrapper around MIUI's `Select` component that integrates with React Hook Form.

   ```tsx
    <RHFSelectPro
      name="favoriteFruits"
      options={[
        { label: "Apple", value: "apple" },
        { label: "Banana", value: "banana" },
        { label: "Cherry", value: "cherry", disabled: true },
      ]}
      control={control} // Optional, if useFormContext is not used
      multiple // if you want
    />
  ```
 Types:

  ```ts
    export type NotUndefined = object | string | number | boolean | null | NotUndefined[];  

    export interface SelectOptionBase {
      label: string;
      value: unknown;
      disabled?: boolean;
    }

    interface OptionItem extends SelectOptionBase {
      /** The value of the option, which can be any type */
      value: NotUndefined; // Different between RHFSelect and RHFSelectPro
    }
  ```


  | Prop                | Type              | Default    | Definition                                                                                                |
  | ------------        | ---------         | -------    | -------------------------------------------------------------------------------------------------------   |
  | name\*              | `Path<T>`         |            | The name of the input                                                                                     |
  | options\*           | OptionItem[ ]     |            | An array of option items to be displayed in the select dropdown                                           |
  | inputDir            | (`ltr` or `rtl`)  |            | If we want the direction of the input to be different from the overall direction of the page. This is mostly used by Persian/Arabic/... when they want to input numbers or English content in an input field |
  | control             | `Control`         |            | The control object from React Hook Form, optional if useFormContext is used                               |
  | maxHeight           | number            |            | The maximum height of the select input                                                                    | 
  | dropDownMaxHeight   | number            |            | The maximum height of the dropdown menu                                                                   |
  | props               | SelectProps       |            | Additional props passed to the underlying MUI `Select`                                                    |

    
--------------------------------

  ### `RHFSwitch`

  `RHFSwitch` is a wrapper around MIUI's `Switch` component that integrates with React Hook Form.

   ```tsx
    <RHFSwitch
      name="notifications"
      label="Enable Notifications"
      control={control} // Optional, if useFormContext is not used
    />
  ```
  | Prop         | Type              | Default    | Definition                                                                                              |
  | ------------ | ---------         | -------    | ------------------------------------------------------------------------------------------------------- |
  | name\*       | `Path<T>`         |            | The name of the input                                                                                   |
  | label\*      | string            |            | The label that will be displayed alongside the switch                                                   |
  | control      | `Control`         |            | The control object from React Hook Form, optional if useFormContext is used                             |
  | props        | SwitchProps     |            | Additional props to pass down to the `Switch` component                                                   |

--------------------------------

  ### `RHFDatePickerJalali`

  `RHFDatePickerJalali` is a date picker component integrated with React Hook Form

   ```tsx
    <RHFDatePickerJalali
      name="birthDate"
      label="Birth Date"
      control={control} // Optional if useFormContext is used
      isReadOnly={false}
    />
  ```

  | Prop         | Type                      | Default    | Definition                                                                                              |
  | ------------ | ---------                 | -------    | ------------------------------------------------------------------------------------------------------- |
  | name\*       | `Path<T>`                 |            | The name of the input                                                                                   |
  | control      | `Control`                 |            | The control object from React Hook Form, optional if useFormContext is used                             |
  | isReadOnly   | boolean                   |   False    | The `isReadOnly` prop to make the input read-only                                                       |
  | props        | `DatePickerProps<Date>`   |            | Additional props passed to the underlying MUI `DatePicker`                                              |

--------------------------------

  ### `RHFDateTimePickerJalali`

  `RHFDateTimePickerJalali` is a date and time picker component integrated with React Hook Form.

   ```tsx
    <RHFDateTimePickerJalali
      name="appointmentTime"
      label="Appointment Time"
      control={control} // Optional if useFormContext is used
      isReadOnly={false}
    />
  ```
  | Prop         | Type                          | Default    | Definition                                                                                              |
  | ------------ | ---------                     | -------    | ------------------------------------------------------------------------------------------------------- |
  | name\*       | `Path<T>`                     |            | The name of the input                                                                                   |
  | control      | `Control`                     |            | The control object from React Hook Form, optional if useFormContext is used                             |
  | isReadOnly   | boolean                       |   False    | The `isReadOnly` prop to make the input read-only                                                       |
  | props        | `DateTimePickerProps<Date>`   |            | Additional props passed to the underlying MUI `DateTimePicker`                                          |


  - It allows for customization of the time view using the `renderTimeViewClock`.
  - It uses `AdapterDateFnsJalali` for Jalali (Persian) calendar support.

## Example

Here is an example that demonstrates how to use all components in a controlled manner. In this example, MUI is used throughout.

Additionally, Zod is used for validation.

```tsx
  import { Stack, Button, FormLabel } from "@mui/material";
  import { z } from "zod";
  import { zodResolver } from "@hookform/resolvers/zod";
  import type { SubmitHandler } from "react-hook-form";
  import { useForm } from "react-hook-form";
  import {
    RHFAutoComplete,
    RHFCheckBox,
    RHFDatePickerJalali,
    RHFDateTimePickerJalali,
    RHFRadioGroup,
    RHFSelect,
    RHFSelectPro,
    RHFSwitch,
    RHFTextField,
    RHFTextMasked
  } from "@paratco/rhf-mui-form";

  const schema = z.object({
    movies: z.string().nullable(),
    isMan: z.boolean(),
    // If you don't want to give a specific default value for the date, you should use nullable here and handle it inside refine with superRefine
    birthDate: z.date().nullable(), 
    startDate: z.date().nullable(),
    fav: z.string(),
    uni: z.string(),
    selects: z.array(z.boolean()),
    turn: z.boolean(),
    name: z.string(),
    phoneNumber: z.string()
  });

  type SchemaType = z.infer<typeof schema>;

  export default function Home() {
    const { control, handleSubmit } = useForm<SchemaType>({
      resolver: zodResolver(schema),
      defaultValues: {
        movies: null, // Autocomplete
        isMan: false, // checkbox
        birthDate: null, // date picker
        startDate: null, // date time
        fav: "", // radio group
        uni: "", // select
        selects: [], // multi select
        turn: false, // switch
        name: "", // RHFText
        phoneNumber: "" // Mask
      }
    });

    const onSubmit: SubmitHandler<SchemaType> = (data: SchemaType) => {
      console.log(data);
    };

    return (
      <Stack sx={{ flexGrow: 1, width: 1 }}>
        <Stack margin={2} gap={2} component="form" onSubmit={(event) => void handleSubmit(onSubmit)(event)}>
          <RHFAutoComplete<SchemaType>
            control={control}
            name="movies"
            key="movies"
            label="Movies"
            options={[
              { label: "Home Alone", value: "homeAlone" },
              { label: "Inception", value: "inception" }
            ]}
          />
          <RHFCheckBox<SchemaType> name="isMan" control={control} label="Are you man?" key="isMan" />
          <RHFDatePickerJalali<SchemaType>
            name="birthDate"
            control={control}
            key="birthDate"
            label="Birth Date"
            // maxDate={new Date()}
            // minDate={new Date()}
          />
          <RHFDateTimePickerJalali<SchemaType> name="startDate" key="startDate" control={control} label="Start Date" />
          <RHFRadioGroup<SchemaType>
            name="fav"
            key="fav"
            control={control}
            formLabel={<FormLabel sx={{ fontSize: "14px" }}>Favorite</FormLabel>}
            options={[
              { label: "spring", value: "spring" },
              { label: "fall", value: "fall" }
            ]}
          />
          <RHFSelect<SchemaType>
            name="uni"
            key="uni"
            control={control}
            label="University"
            options={[
              { label: "UCL", value: "ucl" },
              { label: "milan", value: "milan" }
            ]}
          />
          <RHFSelectPro<SchemaType>
            name="selects"
            multiple={true}
            control={control}
            label="Selects"
            key="selects"
            options={[
              { label: "No1", value: true },
              { label: "No2", value: false }
            ]}
          />
          <RHFSwitch<SchemaType> name="turn" key="turn" control={control} label="Turn on?" />
          <RHFTextField<SchemaType> name="name" control={control} label="Name" key="name" />
          <RHFTextMasked<SchemaType>
            name="phoneNumber"
            control={control}
            label="phoneNumber"
            key="phoneNumber"
            maskOptions={{ mask: "(000) 000-0000" }}
          />
          <Button type="submit">submit</Button>
        </Stack>
      </Stack>
    );
  }
```
