mui-custom-form 0.1.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,25 +1,4 @@
1
- import { useForm } from "react-hook-form";
2
1
  import React from "react";
3
- export type $option<T = any> = {
4
- icon?: React.ReactNode;
5
- label: string;
6
- value: T;
7
- };
8
- export interface ICustomField<T = string> {
9
- label: string;
10
- name: T extends string ? string : keyof T;
11
- type: "text" | "number" | "single-select" | "multi-select" | "date" | "file";
12
- list?: $option[];
13
- required?: boolean;
14
- otherProps?: any;
15
- span?: number;
16
- }
17
- interface ICustomForm {
18
- fieldsGroups: ICustomField<any>[][];
19
- onSubmit: ReturnType<this["formControl"]["handleSubmit"]>;
20
- formControl: ReturnType<typeof useForm<any>>;
21
- submitButton?: boolean;
22
- otherProps?: any;
23
- }
24
- export declare const CustomForm: React.FC<ICustomForm>;
25
- export {};
2
+ import { FieldValues } from "react-hook-form";
3
+ import { ICustomForm } from "./types";
4
+ export declare const CustomForm: <T extends FieldValues>({ fieldsGroups, onSubmit, formControl, submitButton, resetButton, actionButtonsPlacement, otherProps, }: ICustomForm<T>) => React.JSX.Element;
@@ -17,18 +17,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.CustomForm = void 0;
18
18
  var material_1 = require("@mui/material");
19
19
  var x_date_pickers_1 = require("@mui/x-date-pickers");
20
- var react_hook_form_1 = require("react-hook-form");
21
20
  var react_1 = __importDefault(require("react"));
21
+ var react_hook_form_1 = require("react-hook-form");
22
22
  var CustomForm = function (_a) {
23
- var fieldsGroups = _a.fieldsGroups, onSubmit = _a.onSubmit, formControl = _a.formControl, _b = _a.submitButton, submitButton = _b === void 0 ? true : _b, otherProps = _a.otherProps;
24
- var control = formControl.control, setValue = formControl.setValue;
23
+ var fieldsGroups = _a.fieldsGroups, onSubmit = _a.onSubmit, formControl = _a.formControl, _b = _a.submitButton, submitButton = _b === void 0 ? true : _b, resetButton = _a.resetButton, actionButtonsPlacement = _a.actionButtonsPlacement, otherProps = _a.otherProps;
24
+ var control = formControl.control, setValue = formControl.setValue, reset = formControl.reset, register = formControl.register;
25
25
  var renderField = function (field) {
26
26
  switch (field.type) {
27
27
  case "text":
28
28
  case "number":
29
- return (react_1.default.createElement(react_hook_form_1.Controller, { name: field.name, control: control, render: function (_a) {
29
+ return (react_1.default.createElement(react_hook_form_1.Controller, { name: field.name, control: control, rules: { required: field.required }, render: function (_a) {
30
30
  var controlField = _a.field, error = _a.fieldState.error;
31
- return (react_1.default.createElement(material_1.TextField, __assign({}, controlField, field.otherProps, { label: field.label, type: field.type, fullWidth: true, required: field.required, error: !!error, helperText: error === null || error === void 0 ? void 0 : error.message })));
31
+ return (react_1.default.createElement(material_1.TextField, __assign({}, controlField, { value: controlField.value || "" }, field.otherProps, { label: field.label, type: field.type, fullWidth: true, required: field.required, error: !!error, helperText: error === null || error === void 0 ? void 0 : error.message, onChange: function (e) {
32
+ if (!e.target.value)
33
+ return controlField.onChange(undefined);
34
+ controlField.onChange(field.type === "number"
35
+ ? parseFloat(e.target.value)
36
+ : e.target.value);
37
+ } })));
32
38
  } }));
33
39
  case "single-select":
34
40
  case "multi-select":
@@ -37,13 +43,14 @@ var CustomForm = function (_a) {
37
43
  var controlField = _a.field, error = _a.fieldState.error;
38
44
  return (react_1.default.createElement(material_1.FormControl, { fullWidth: true, error: !!error },
39
45
  react_1.default.createElement(material_1.InputLabel, { required: field.required }, field.label),
40
- react_1.default.createElement(material_1.Select, __assign({}, controlField, field.otherProps, { multiple: field.type === "multi-select" }), (_b = field.list) === null || _b === void 0 ? void 0 : _b.map(function (item) { return (react_1.default.createElement(material_1.MenuItem, { key: item.value, value: item.value }, item.label)); })),
46
+ react_1.default.createElement(material_1.Select, __assign({ label: field.label }, controlField, { value: controlField.value ||
47
+ (field.type === "multi-select" ? [] : "") }, field.otherProps, { multiple: field.type === "multi-select" }), (_b = field.list) === null || _b === void 0 ? void 0 : _b.map(function (item) { return (react_1.default.createElement(material_1.MenuItem, { key: item.value, value: item.value }, item.label)); })),
41
48
  error && react_1.default.createElement(material_1.FormHelperText, null, error.message)));
42
49
  } }));
43
50
  case "date":
44
51
  return (react_1.default.createElement(react_hook_form_1.Controller, { name: field.name, control: control, render: function (_a) {
45
52
  var controlField = _a.field, error = _a.fieldState.error;
46
- return (react_1.default.createElement(x_date_pickers_1.DatePicker, __assign({}, controlField, field.otherProps, { label: field.label, onChange: controlField.onChange, slotProps: {
53
+ return (react_1.default.createElement(x_date_pickers_1.DatePicker, __assign({}, controlField, { value: controlField.value || null }, field.otherProps, { label: field.label, onChange: controlField.onChange, slotProps: {
47
54
  textField: {
48
55
  required: field.required,
49
56
  error: !!error,
@@ -68,9 +75,13 @@ var CustomForm = function (_a) {
68
75
  return null;
69
76
  }
70
77
  };
71
- return (react_1.default.createElement(material_1.Stack, __assign({ component: "form", onSubmit: onSubmit, noValidate: true }, otherProps, { spacing: 3 }),
78
+ var submitButtonProps = submitButton && submitButton !== true ? submitButton : {};
79
+ var resetButtonProps = resetButton && resetButton !== true ? resetButton : {};
80
+ return (react_1.default.createElement(material_1.Stack, __assign({ component: "form", onSubmit: formControl.handleSubmit(onSubmit[0], onSubmit[1]), noValidate: true }, otherProps, { spacing: 3 }),
72
81
  react_1.default.createElement(material_1.Grid, { container: true, spacing: 1 }, fieldsGroups.map(function (fields, rowIndex) { return (react_1.default.createElement(material_1.Grid, { container: true, item: true, key: rowIndex, spacing: 2 }, fields.map(function (field, fieldIndex) { return (react_1.default.createElement(material_1.Grid, { item: true, key: fieldIndex, xs: field.span || true }, renderField(field))); }))); })),
73
- submitButton && (react_1.default.createElement(material_1.Button, { type: "submit", variant: "contained" }, "Submit"))));
82
+ react_1.default.createElement(material_1.Stack, { direction: "row", justifyContent: actionButtonsPlacement },
83
+ resetButton && (react_1.default.createElement(material_1.Button, __assign({ type: "reset", variant: "contained", onClick: function () { return reset(); } }, resetButtonProps), "Reset")),
84
+ submitButton && (react_1.default.createElement(material_1.Button, __assign({ type: "submit", variant: "contained" }, submitButtonProps), "Submit")))));
74
85
  };
75
86
  exports.CustomForm = CustomForm;
76
87
  //# sourceMappingURL=CustomForm.js.map
@@ -0,0 +1,42 @@
1
+ import { ButtonProps } from "@mui/material";
2
+ import { CSSProperties } from "react";
3
+ import { FieldValues, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
4
+ type name<T> = T extends string ? string : keyof T;
5
+ type SelectTypes = "single-select" | "multi-select";
6
+ type BaseTypes = "text" | "number" | "date" | "file";
7
+ export interface ISelectField {
8
+ type: SelectTypes;
9
+ list: $option[];
10
+ }
11
+ export interface IOtherField {
12
+ type: BaseTypes;
13
+ list?: $option[];
14
+ }
15
+ export type ICustomField<T = string> = {
16
+ label: string;
17
+ name: name<T> | name<T>[];
18
+ required?: true;
19
+ otherProps?: any;
20
+ span?: number;
21
+ } & ({
22
+ type: SelectTypes;
23
+ list: $option[];
24
+ } | {
25
+ type: BaseTypes;
26
+ });
27
+ export type $option<T = any> = {
28
+ icon?: React.ReactNode;
29
+ label: string;
30
+ value: T;
31
+ };
32
+ export type IFieldGroup<T = any> = ICustomField<T>[][];
33
+ export interface ICustomForm<T extends FieldValues> {
34
+ fieldsGroups: IFieldGroup<any>;
35
+ onSubmit: [SubmitHandler<T>, SubmitErrorHandler<T>?];
36
+ formControl: ReturnType<typeof useForm<T>>;
37
+ actionButtonsPlacement?: CSSProperties["justifyContent"];
38
+ submitButton?: ButtonProps | boolean;
39
+ resetButton?: ButtonProps | boolean;
40
+ otherProps?: any;
41
+ }
42
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mui-custom-form",
3
- "version": "0.1.8",
3
+ "version": "1.0.0",
4
4
  "description": "A versatile React form component utilizing MUI components and react-hook-form.",
5
5
  "main": "dist/CustomForm.js",
6
6
  "types": "dist/CustomForm.d.ts",
package/readme.md CHANGED
@@ -1,84 +1,76 @@
1
- ## CustomForm
1
+ # 📝 CustomForm
2
2
 
3
- `CustomForm` is a versatile React form component designed to streamline the form-building process. Utilizing the power of MUI components and the flexibility of `react-hook-form`, this package offers an intuitive way to create dynamic and responsive forms.
3
+ `CustomForm` is a versatile React form component designed to simplify your form-building journey. Powered by MUI components and the flexibility of `react-hook-form`, this package offers an intuitive way to create dynamic and responsive forms.
4
4
 
5
- ### Table of Contents
5
+ ## 📚 Table of Contents
6
6
 
7
- - [Installation](#installation)
8
- - [Features](#features)
9
- - [Usage](#usage)
10
- - [Props](#props)
11
- - [Example](#example)
12
- - [Contribution](#contribution)
13
- - [License](#license)
7
+ - [🛠 Installation](#installation)
8
+ - [Features](#features)
9
+ - [🚀 Usage](#usage)
10
+ - [🔧 Props](#props)
11
+ - [📖 Examples](#examples)
12
+ - [🤝 Contribution](#contribution)
13
+ - [📜 License](#license)
14
14
 
15
- ### Installation
16
-
17
- To add `CustomForm` to your project, use the following command:
15
+ ## 🛠 Installation
18
16
 
19
17
  ```bash
20
18
  npm install mui-custom-form
21
19
  ```
22
20
 
23
- Ensure that you have the required peer dependencies installed:
21
+ 📦 **Dependencies**:
24
22
 
25
23
  - `@mui/material`
26
24
  - `@mui/x-date-pickers`
27
25
  - `react-hook-form`
28
26
 
29
- ### Features
30
-
31
- - **Dynamic Field Types**: Supports text, number, single-select, multi-select, date, and file input types.
32
- - **MUI Integration**: Seamless integration with MUI components for a consistent and professional look.
33
- - **Responsive Design**: Adaptable grid system, ensuring your form looks great on all devices.
34
-
35
- ### Usage
27
+ ## Features
36
28
 
37
- #### Props
29
+ - 🎛 **Dynamic Field Types**
30
+ - 🎨 **MUI Integration**
31
+ - 📱 **Responsive Design**
38
32
 
39
- - CustomForm component
33
+ ## 🚀 Usage
40
34
 
41
- | Name | Description |
42
- | -------------- | -------------------------------------------------------- |
43
- | `fieldsGroups` | 2D array representing groups of fields in the form. |
44
- | `onSubmit` | Function called upon form submission. |
45
- | `formControl` | Control object from `react-hook-form`. |
46
- | `submitButton` | Boolean to toggle the visibility of the submit button. |
47
- | `otherProps` | Any additional props to pass down to the form component. |
35
+ ### 🔧 Props
48
36
 
49
- - CustomField 2D array
37
+ #### CustomForm Component
50
38
 
51
- The `ICustomField` interface is used to define each field in the form. Below are its properties:
39
+ | Name | Description | Is Required? |
40
+ | ------------------------ | ------------------------------------------------------------------ | ------------ |
41
+ | `fieldsGroups` | 2D array representing groups of fields in the form. | Yes |
42
+ | `onSubmit` | Array containing functions for form submission and error handling. | Yes |
43
+ | `formControl` | Control object from `react-hook-form`. | Yes |
44
+ | `submitButton` | Boolean to toggle the visibility of the submit button. | No |
45
+ | `resetButton` | Boolean to toggle the visibility of the reset button. | No |
46
+ | `actionButtonsPlacement` | CSS property for button placement. | No |
47
+ | `otherProps` | Any additional props to pass down to the form component. | No |
52
48
 
53
- | Name | Description |
54
- | ------------ | ----------------------------------------------------------------------------------------- |
55
- | `label` | The display label of the field. |
56
- | `name` | The name attribute of the field, used for form data identification. |
57
- | `type` | The type of the field, determining its behavior and appearance. |
58
- | `list` | An array of options for select type fields. Each option has a label and a value. |
59
- | `required` | Indicates whether the field is mandatory or not. |
60
- | `otherProps` | Any additional props to pass to the underlying MUI component. |
61
- | `span` | A number between 1 and 12, representing the grid span for the field in MUI's grid system. |
49
+ #### CustomField 2D Array
62
50
 
63
- The `list` array, has the following structure:
51
+ | Name | Description | Is Required? |
52
+ | ------------ | ------------------------------------------- | ------------ |
53
+ | `label` | The display label of the field. | Yes |
54
+ | `name` | The name attribute of the field. | Yes |
55
+ | `type` | The type of the field. | Yes |
56
+ | `list` | An array of options for select type fields. | Conditional |
57
+ | `required` | Is the field mandatory? | No |
58
+ | `otherProps` | Any additional props. | No |
59
+ | `span` | Grid span for the field. | No |
64
60
 
65
- | Name | Description |
66
- | ------- | ---------------------------------------------------------- |
67
- | `icon` | An optional icon to display alongside the option. |
68
- | `label` | The display label of the option. |
69
- | `value` | The value of the option, used when the option is selected. |
61
+ #### \* `list` prop is required when the type is `single-select` or `multi-select`.
70
62
 
71
63
  ---
72
64
 
73
- ### Example
65
+ ## 📖 Examples
74
66
 
75
- Here's a simple example showcasing how to implement a `CustomForm`:
67
+ ### Basic Form
76
68
 
77
- ```typescript
78
- const MyComponent = () => {
79
- const formControl = useForm()
69
+ ```ts
70
+ const BasicForm = () => {
71
+ const formControl = useForm<{ username: string; birthdate: string }>()
80
72
 
81
- const fieldsGroups: ICustomField[][] = [
73
+ const fieldsGroups: IFieldGroup = [
82
74
  [
83
75
  {
84
76
  label: "Username",
@@ -90,40 +82,52 @@ const MyComponent = () => {
90
82
  label: "Birthdate",
91
83
  name: "birthdate",
92
84
  type: "date",
85
+ required: true,
93
86
  },
94
87
  ],
95
88
  ]
96
89
 
97
- const handleSubmit = (data: unknown) => {
90
+ const handleSubmit = (data: { username: string; birthdate: string }) => {
98
91
  console.log({ success: data })
99
92
  }
100
93
 
94
+ const submitError = (data: unknown) => {
95
+ console.log({ error: data })
96
+ }
97
+
101
98
  return (
102
99
  <CustomForm
103
100
  fieldsGroups={fieldsGroups}
104
- onSubmit={formControl.handleSubmit(handleSubmit)}
101
+ onSubmit={[handleSubmit, submitError]}
105
102
  formControl={formControl}
106
103
  />
107
104
  )
108
105
  }
109
106
  ```
110
107
 
111
- Usage with zod
108
+ ### Form with Zod Validation
109
+
110
+ ```ts
111
+ const GENDERS = ["Male", "Female"] as const
112
+ const HOBBIES = ["Coding", "Collections", "Hiking"] as const
112
113
 
113
- ```typescript
114
114
  const Fields = z.object({
115
115
  username: z.string(),
116
- age: z.string(),
116
+ age: z.number().min(16).max(80),
117
+ gender: z.enum(GENDERS),
118
+ hobbies: z.array(z.enum(HOBBIES)).nonempty("Please choose one"),
119
+ birthDate: z.date(),
120
+ file: z.instanceof(File).optional(),
117
121
  })
118
122
 
119
123
  type FieldTypes = z.infer<typeof Fields>
120
124
 
121
- function MyComponent() {
125
+ const FormWithZod = () => {
122
126
  const formControl = useForm<FieldTypes>({
123
127
  resolver: zodResolver(Fields),
124
128
  })
125
129
 
126
- const fieldsGroups: ICustomField<FieldTypes>[][] = [
130
+ const fieldsGroups: IFieldGroup<FieldTypes> = [
127
131
  [
128
132
  {
129
133
  label: "Username",
@@ -138,6 +142,33 @@ function MyComponent() {
138
142
  required: true,
139
143
  },
140
144
  ],
145
+ [
146
+ {
147
+ label: "Gender",
148
+ name: "gender",
149
+ type: "single-select",
150
+ list: GENDERS.map((gender) => ({ label: gender, value: gender })),
151
+ },
152
+ {
153
+ label: "Hobbies",
154
+ name: "hobbies",
155
+ type: "multi-select",
156
+ list: HOBBIES.map((hobby) => ({ label: hobby, value: hobby })),
157
+ required: true,
158
+ },
159
+ ],
160
+ [
161
+ {
162
+ label: "Date of birth",
163
+ name: "birthDate",
164
+ type: "date",
165
+ },
166
+ {
167
+ label: "Upload Image",
168
+ name: "file",
169
+ type: "file",
170
+ },
171
+ ],
141
172
  ]
142
173
 
143
174
  const onSubmit = (data: FieldTypes) => {
@@ -147,17 +178,15 @@ function MyComponent() {
147
178
  return (
148
179
  <CustomForm
149
180
  fieldsGroups={fieldsGroups}
150
- onSubmit={formControl.handleSubmit(onSubmit, (fail) =>
151
- console.log({ fail })
152
- )}
181
+ onSubmit={[onSubmit]}
153
182
  formControl={formControl}
154
183
  />
155
184
  )
156
185
  }
157
186
  ```
158
187
 
159
- ### Contribution
188
+ ---
160
189
 
161
- Your contributions are always welcome! Please create a pull request and we'll review your suggestion.
190
+ ## 🤝 Contribution
162
191
 
163
- ---
192
+ Your contributions are always welcome!