mui-custom-form 0.1.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.
@@ -0,0 +1,25 @@
1
+ import { useForm } from "react-hook-form";
2
+ 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>;
21
+ submitButton?: boolean;
22
+ otherProps?: any;
23
+ }
24
+ export declare const CustomForm: React.FC<ICustomForm>;
25
+ export {};
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.CustomForm = void 0;
18
+ var material_1 = require("@mui/material");
19
+ var x_date_pickers_1 = require("@mui/x-date-pickers");
20
+ var react_hook_form_1 = require("react-hook-form");
21
+ var react_1 = __importDefault(require("react"));
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;
25
+ var renderField = function (field) {
26
+ switch (field.type) {
27
+ case "text":
28
+ case "number":
29
+ return (react_1.default.createElement(react_hook_form_1.Controller, { name: field.name, control: control, render: function (_a) {
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 })));
32
+ } }));
33
+ case "single-select":
34
+ case "multi-select":
35
+ return (react_1.default.createElement(react_hook_form_1.Controller, { name: field.name, control: control, render: function (_a) {
36
+ var _b;
37
+ var controlField = _a.field, error = _a.fieldState.error;
38
+ return (react_1.default.createElement(material_1.FormControl, { fullWidth: true, error: !!error },
39
+ 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)); })),
41
+ error && react_1.default.createElement(material_1.FormHelperText, null, error.message)));
42
+ } }));
43
+ case "date":
44
+ return (react_1.default.createElement(react_hook_form_1.Controller, { name: field.name, control: control, render: function (_a) {
45
+ 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: {
47
+ textField: {
48
+ required: field.required,
49
+ error: !!error,
50
+ helperText: error === null || error === void 0 ? void 0 : error.message,
51
+ fullWidth: true,
52
+ },
53
+ } })));
54
+ } }));
55
+ case "file":
56
+ return (react_1.default.createElement(react_1.default.Fragment, null,
57
+ react_1.default.createElement(material_1.FormLabel, { component: "legend" }, field.label),
58
+ react_1.default.createElement(material_1.Button, { variant: "contained", component: "label" },
59
+ "Upload File",
60
+ react_1.default.createElement("input", { type: "file", hidden: true, onChange: function (e) {
61
+ // Set the value to either the File instance or undefined
62
+ var fileValue = e.target.files && e.target.files.length > 0
63
+ ? e.target.files[0]
64
+ : undefined;
65
+ setValue(field.name, fileValue);
66
+ } }))));
67
+ default:
68
+ return null;
69
+ }
70
+ };
71
+ return (react_1.default.createElement(material_1.Stack, __assign({ component: "form", onSubmit: onSubmit, noValidate: true }, otherProps, { spacing: 3 }),
72
+ 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"))));
74
+ };
75
+ exports.CustomForm = CustomForm;
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "mui-custom-form",
3
+ "version": "0.1.0",
4
+ "description": "A versatile React form component utilizing MUI components and react-hook-form.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/DevOsamaIslam/muicustom-form.git"
14
+ },
15
+ "keywords": [
16
+ "react",
17
+ "form",
18
+ "MUI",
19
+ "react-hook-form",
20
+ "customizable"
21
+ ],
22
+ "author": "Your Name",
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/your-github-username/custom-form/issues"
26
+ },
27
+ "homepage": "https://github.com/your-github-username/custom-form#readme",
28
+ "peerDependencies": {
29
+ "@mui/material": "5.14.2",
30
+ "@mui/x-date-pickers": "^6.10.2",
31
+ "react-hook-form": "^7.45.2"
32
+ },
33
+ "devDependencies": {
34
+ "@emotion/react": "^11.11.1",
35
+ "@emotion/styled": "^11.11.0",
36
+ "@types/node": "^20.5.1",
37
+ "@types/react": "^18.2.20",
38
+ "typescript": "^5.1.6"
39
+ }
40
+ }
package/readme.md ADDED
@@ -0,0 +1,94 @@
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
+ | Name | Description |
40
+ | -------------- | -------------------------------------------------------- |
41
+ | `fieldsGroups` | 2D array representing groups of fields in the form. |
42
+ | `onSubmit` | Function called upon form submission. |
43
+ | `formControl` | Control object from `react-hook-form`. |
44
+ | `submitButton` | Boolean to toggle the visibility of the submit button. |
45
+ | `otherProps` | Any additional props to pass down to the form component. |
46
+
47
+ ### Example
48
+
49
+ Here's a simple example showcasing how to implement a `CustomForm`:
50
+
51
+ ```typescript
52
+ import React from "react"
53
+ import { CustomForm } from "your-package-name"
54
+
55
+ const MyComponent = () => {
56
+ const formControl = useForm()
57
+
58
+ const fieldsGroups = [
59
+ [
60
+ {
61
+ label: "Username",
62
+ name: "username",
63
+ type: "text",
64
+ required: true,
65
+ },
66
+ {
67
+ label: "Birthdate",
68
+ name: "birthdate",
69
+ type: "date",
70
+ },
71
+ ],
72
+ ]
73
+
74
+ const handleSubmit = (data) => {
75
+ console.log(data)
76
+ }
77
+
78
+ return (
79
+ <CustomForm
80
+ fieldsGroups={fieldsGroups}
81
+ onSubmit={handleSubmit}
82
+ formControl={formControl}
83
+ />
84
+ )
85
+ }
86
+
87
+ export default MyComponent
88
+ ```
89
+
90
+ ### Contribution
91
+
92
+ Your contributions are always welcome! Please create a pull request and we'll review your suggestion.
93
+
94
+ ---
@@ -0,0 +1,171 @@
1
+ import {
2
+ Button,
3
+ FormControl,
4
+ FormHelperText,
5
+ FormLabel,
6
+ Grid,
7
+ InputLabel,
8
+ MenuItem,
9
+ Select,
10
+ Stack,
11
+ TextField,
12
+ } from "@mui/material"
13
+ import { DatePicker } from "@mui/x-date-pickers"
14
+ import { Controller } from "react-hook-form"
15
+ import { useForm } from "react-hook-form"
16
+ import React from "react"
17
+
18
+ export type $option<T = any> = {
19
+ icon?: React.ReactNode
20
+ label: string
21
+ value: T
22
+ }
23
+
24
+ export interface ICustomField<T = string> {
25
+ label: string
26
+ name: T extends string ? string : keyof T
27
+ type: "text" | "number" | "single-select" | "multi-select" | "date" | "file"
28
+ list?: $option[]
29
+ required?: boolean // This can be further refined to your validation rules
30
+ otherProps?: any
31
+ span?: number // A number between 1 and 12, representing MUI's grid system
32
+ }
33
+
34
+ interface ICustomForm {
35
+ fieldsGroups: ICustomField<any>[][]
36
+ onSubmit: ReturnType<this["formControl"]["handleSubmit"]>
37
+ formControl: ReturnType<typeof useForm>
38
+ submitButton?: boolean
39
+ otherProps?: any
40
+ }
41
+
42
+ export const CustomForm: React.FC<ICustomForm> = ({
43
+ fieldsGroups,
44
+ onSubmit,
45
+ formControl,
46
+ submitButton = true,
47
+ otherProps,
48
+ }) => {
49
+ const { control, setValue } = formControl
50
+
51
+ const renderField = (field: ICustomField<string>) => {
52
+ switch (field.type) {
53
+ case "text":
54
+ case "number":
55
+ return (
56
+ <Controller
57
+ name={field.name}
58
+ control={control}
59
+ render={({ field: controlField, fieldState: { error } }) => (
60
+ <TextField
61
+ {...controlField}
62
+ {...field.otherProps}
63
+ label={field.label}
64
+ type={field.type}
65
+ fullWidth
66
+ required={field.required}
67
+ error={!!error}
68
+ helperText={error?.message}
69
+ />
70
+ )}
71
+ />
72
+ )
73
+ case "single-select":
74
+ case "multi-select":
75
+ return (
76
+ <Controller
77
+ name={field.name}
78
+ control={control}
79
+ render={({ field: controlField, fieldState: { error } }) => (
80
+ <FormControl fullWidth error={!!error}>
81
+ <InputLabel required={field.required}>{field.label}</InputLabel>
82
+ <Select
83
+ {...controlField}
84
+ {...field.otherProps}
85
+ multiple={field.type === "multi-select"}>
86
+ {field.list?.map((item) => (
87
+ <MenuItem key={item.value} value={item.value}>
88
+ {item.label}
89
+ </MenuItem>
90
+ ))}
91
+ </Select>
92
+ {error && <FormHelperText>{error.message}</FormHelperText>}
93
+ </FormControl>
94
+ )}
95
+ />
96
+ )
97
+ case "date":
98
+ return (
99
+ <Controller
100
+ name={field.name}
101
+ control={control}
102
+ render={({ field: controlField, fieldState: { error } }) => (
103
+ <DatePicker
104
+ {...controlField}
105
+ {...field.otherProps}
106
+ label={field.label}
107
+ onChange={controlField.onChange}
108
+ slotProps={{
109
+ textField: {
110
+ required: field.required,
111
+ error: !!error,
112
+ helperText: error?.message,
113
+ fullWidth: true,
114
+ },
115
+ }}
116
+ />
117
+ )}
118
+ />
119
+ )
120
+ case "file":
121
+ return (
122
+ <>
123
+ <FormLabel component="legend">{field.label}</FormLabel>
124
+ <Button variant="contained" component="label">
125
+ Upload File
126
+ <input
127
+ type="file"
128
+ hidden
129
+ onChange={(e) => {
130
+ // Set the value to either the File instance or undefined
131
+ const fileValue =
132
+ e.target.files && e.target.files.length > 0
133
+ ? e.target.files[0]
134
+ : undefined
135
+ setValue(field.name, fileValue)
136
+ }}
137
+ />
138
+ </Button>
139
+ </>
140
+ )
141
+ default:
142
+ return null
143
+ }
144
+ }
145
+
146
+ return (
147
+ <Stack
148
+ component="form"
149
+ onSubmit={onSubmit}
150
+ noValidate
151
+ {...otherProps}
152
+ spacing={3}>
153
+ <Grid container spacing={1}>
154
+ {fieldsGroups.map((fields, rowIndex) => (
155
+ <Grid container item key={rowIndex} spacing={2}>
156
+ {fields.map((field, fieldIndex) => (
157
+ <Grid item key={fieldIndex} xs={field.span || true}>
158
+ {renderField(field as unknown as ICustomField<string>)}
159
+ </Grid>
160
+ ))}
161
+ </Grid>
162
+ ))}
163
+ </Grid>
164
+ {submitButton && (
165
+ <Button type="submit" variant="contained">
166
+ Submit
167
+ </Button>
168
+ )}
169
+ </Stack>
170
+ )
171
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "module": "commonjs",
5
+ "jsx": "react",
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "strict": true,
9
+ "esModuleInterop": true
10
+ },
11
+ "include": ["src/**/*"],
12
+ "exclude": ["node_modules"]
13
+ }