mui-custom-form 0.1.8 → 1.0.1

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.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # 📝 CustomForm
2
+
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
+
5
+ ## 📚 Table of Contents
6
+
7
+ - [🛠 Installation](#installation)
8
+ - [✨ Features](#features)
9
+ - [🚀 Usage](#usage)
10
+ - [🔧 Props](#props)
11
+ - [📖 Examples](#examples)
12
+ - [🤝 Contribution](#contribution)
13
+ - [📜 License](#license)
14
+
15
+ ## 🛠 Installation
16
+
17
+ ```bash
18
+ npm install mui-custom-form
19
+ ```
20
+
21
+ 📦 **Dependencies**:
22
+
23
+ - `@mui/material`
24
+ - `@mui/x-date-pickers`
25
+ - `react-hook-form`
26
+
27
+ ## ✨ Features
28
+
29
+ - 🎛 **Dynamic Field Types**
30
+ - 🎨 **MUI Integration**
31
+ - 📱 **Responsive Design**
32
+
33
+ ## 🚀 Usage
34
+
35
+ ### 🔧 Props
36
+
37
+ #### CustomForm Component
38
+
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 |
48
+
49
+ #### CustomField 2D Array
50
+
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 |
60
+
61
+ #### \* `list` prop is required when the type is `single-select` or `multi-select`.
62
+
63
+ ---
64
+
65
+ ## 📖 Examples
66
+
67
+ ### Basic Form
68
+
69
+ ```ts
70
+ const BasicForm = () => {
71
+ const formControl = useForm<{ username: string; birthdate: string }>()
72
+
73
+ const fieldsGroups: IFieldGroup = [
74
+ [
75
+ {
76
+ label: "Username",
77
+ name: "username",
78
+ type: "text",
79
+ required: true,
80
+ },
81
+ {
82
+ label: "Birthdate",
83
+ name: "birthdate",
84
+ type: "date",
85
+ required: true,
86
+ },
87
+ ],
88
+ ]
89
+
90
+ const handleSubmit = (data: { username: string; birthdate: string }) => {
91
+ console.log({ success: data })
92
+ }
93
+
94
+ const submitError = (data: unknown) => {
95
+ console.log({ error: data })
96
+ }
97
+
98
+ return (
99
+ <CustomForm
100
+ fieldsGroups={fieldsGroups}
101
+ onSubmit={[handleSubmit, submitError]}
102
+ formControl={formControl}
103
+ />
104
+ )
105
+ }
106
+ ```
107
+
108
+ ### Form with Zod Validation
109
+
110
+ ```ts
111
+ const GENDERS = ["Male", "Female"] as const
112
+ const HOBBIES = ["Coding", "Collections", "Hiking"] as const
113
+
114
+ const Fields = z.object({
115
+ username: 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(),
121
+ })
122
+
123
+ type FieldTypes = z.infer<typeof Fields>
124
+
125
+ const FormWithZod = () => {
126
+ const formControl = useForm<FieldTypes>({
127
+ resolver: zodResolver(Fields),
128
+ })
129
+
130
+ const fieldsGroups: IFieldGroup<FieldTypes> = [
131
+ [
132
+ {
133
+ label: "Username",
134
+ name: "username",
135
+ type: "text",
136
+ required: true,
137
+ },
138
+ {
139
+ label: "Age",
140
+ name: "age",
141
+ type: "number",
142
+ required: true,
143
+ },
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
+ ],
172
+ ]
173
+
174
+ const onSubmit = (data: FieldTypes) => {
175
+ console.log({ success: data })
176
+ }
177
+
178
+ return (
179
+ <CustomForm
180
+ fieldsGroups={fieldsGroups}
181
+ onSubmit={[onSubmit]}
182
+ formControl={formControl}
183
+ />
184
+ )
185
+ }
186
+ ```
187
+
188
+ ---
189
+
190
+ ## 🤝 Contribution
191
+
192
+ Your contributions are always welcome!
@@ -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.1",
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 DELETED
@@ -1,163 +0,0 @@
1
- ## CustomForm
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.
4
-
5
- ### Table of Contents
6
-
7
- - [Installation](#installation)
8
- - [Features](#features)
9
- - [Usage](#usage)
10
- - [Props](#props)
11
- - [Example](#example)
12
- - [Contribution](#contribution)
13
- - [License](#license)
14
-
15
- ### Installation
16
-
17
- To add `CustomForm` to your project, use the following command:
18
-
19
- ```bash
20
- npm install mui-custom-form
21
- ```
22
-
23
- Ensure that you have the required peer dependencies installed:
24
-
25
- - `@mui/material`
26
- - `@mui/x-date-pickers`
27
- - `react-hook-form`
28
-
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
36
-
37
- #### Props
38
-
39
- - CustomForm component
40
-
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. |
48
-
49
- - CustomField 2D array
50
-
51
- The `ICustomField` interface is used to define each field in the form. Below are its properties:
52
-
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. |
62
-
63
- The `list` array, has the following structure:
64
-
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. |
70
-
71
- ---
72
-
73
- ### Example
74
-
75
- Here's a simple example showcasing how to implement a `CustomForm`:
76
-
77
- ```typescript
78
- const MyComponent = () => {
79
- const formControl = useForm()
80
-
81
- const fieldsGroups: ICustomField[][] = [
82
- [
83
- {
84
- label: "Username",
85
- name: "username",
86
- type: "text",
87
- required: true,
88
- },
89
- {
90
- label: "Birthdate",
91
- name: "birthdate",
92
- type: "date",
93
- },
94
- ],
95
- ]
96
-
97
- const handleSubmit = (data: unknown) => {
98
- console.log({ success: data })
99
- }
100
-
101
- return (
102
- <CustomForm
103
- fieldsGroups={fieldsGroups}
104
- onSubmit={formControl.handleSubmit(handleSubmit)}
105
- formControl={formControl}
106
- />
107
- )
108
- }
109
- ```
110
-
111
- Usage with zod
112
-
113
- ```typescript
114
- const Fields = z.object({
115
- username: z.string(),
116
- age: z.string(),
117
- })
118
-
119
- type FieldTypes = z.infer<typeof Fields>
120
-
121
- function MyComponent() {
122
- const formControl = useForm<FieldTypes>({
123
- resolver: zodResolver(Fields),
124
- })
125
-
126
- const fieldsGroups: ICustomField<FieldTypes>[][] = [
127
- [
128
- {
129
- label: "Username",
130
- name: "username",
131
- type: "text",
132
- required: true,
133
- },
134
- {
135
- label: "Age",
136
- name: "age",
137
- type: "number",
138
- required: true,
139
- },
140
- ],
141
- ]
142
-
143
- const onSubmit = (data: FieldTypes) => {
144
- console.log({ success: data })
145
- }
146
-
147
- return (
148
- <CustomForm
149
- fieldsGroups={fieldsGroups}
150
- onSubmit={formControl.handleSubmit(onSubmit, (fail) =>
151
- console.log({ fail })
152
- )}
153
- formControl={formControl}
154
- />
155
- )
156
- }
157
- ```
158
-
159
- ### Contribution
160
-
161
- Your contributions are always welcome! Please create a pull request and we'll review your suggestion.
162
-
163
- ---