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.
- package/dist/CustomForm.d.ts +3 -24
- package/dist/CustomForm.js +20 -9
- package/dist/types.d.ts +42 -0
- package/dist/types.js +3 -0
- package/package.json +1 -1
- package/readme.md +95 -66
package/dist/CustomForm.d.ts
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
1
|
-
import { useForm } from "react-hook-form";
|
|
2
1
|
import React from "react";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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;
|
package/dist/CustomForm.js
CHANGED
|
@@ -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({
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/dist/types.d.ts
ADDED
|
@@ -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
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,84 +1,76 @@
|
|
|
1
|
-
|
|
1
|
+
# 📝 CustomForm
|
|
2
2
|
|
|
3
|
-
`CustomForm` is a versatile React form component designed to
|
|
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
|
-
|
|
5
|
+
## 📚 Table of Contents
|
|
6
6
|
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Features](#features)
|
|
9
|
-
- [Usage](#usage)
|
|
10
|
-
- [Props](#props)
|
|
11
|
-
- [
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
📦 **Dependencies**:
|
|
24
22
|
|
|
25
23
|
- `@mui/material`
|
|
26
24
|
- `@mui/x-date-pickers`
|
|
27
25
|
- `react-hook-form`
|
|
28
26
|
|
|
29
|
-
|
|
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
|
-
|
|
29
|
+
- 🎛 **Dynamic Field Types**
|
|
30
|
+
- 🎨 **MUI Integration**
|
|
31
|
+
- 📱 **Responsive Design**
|
|
38
32
|
|
|
39
|
-
|
|
33
|
+
## 🚀 Usage
|
|
40
34
|
|
|
41
|
-
|
|
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
|
-
|
|
37
|
+
#### CustomForm Component
|
|
50
38
|
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
+
## 📖 Examples
|
|
74
66
|
|
|
75
|
-
|
|
67
|
+
### Basic Form
|
|
76
68
|
|
|
77
|
-
```
|
|
78
|
-
const
|
|
79
|
-
const formControl = useForm()
|
|
69
|
+
```ts
|
|
70
|
+
const BasicForm = () => {
|
|
71
|
+
const formControl = useForm<{ username: string; birthdate: string }>()
|
|
80
72
|
|
|
81
|
-
const fieldsGroups:
|
|
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:
|
|
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={
|
|
101
|
+
onSubmit={[handleSubmit, submitError]}
|
|
105
102
|
formControl={formControl}
|
|
106
103
|
/>
|
|
107
104
|
)
|
|
108
105
|
}
|
|
109
106
|
```
|
|
110
107
|
|
|
111
|
-
|
|
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.
|
|
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
|
-
|
|
125
|
+
const FormWithZod = () => {
|
|
122
126
|
const formControl = useForm<FieldTypes>({
|
|
123
127
|
resolver: zodResolver(Fields),
|
|
124
128
|
})
|
|
125
129
|
|
|
126
|
-
const fieldsGroups:
|
|
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={
|
|
151
|
-
console.log({ fail })
|
|
152
|
-
)}
|
|
181
|
+
onSubmit={[onSubmit]}
|
|
153
182
|
formControl={formControl}
|
|
154
183
|
/>
|
|
155
184
|
)
|
|
156
185
|
}
|
|
157
186
|
```
|
|
158
187
|
|
|
159
|
-
|
|
188
|
+
---
|
|
160
189
|
|
|
161
|
-
|
|
190
|
+
## 🤝 Contribution
|
|
162
191
|
|
|
163
|
-
|
|
192
|
+
Your contributions are always welcome!
|