react-form-dto 0.0.4 → 0.0.6
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 +111 -51
- package/dist/react-form-dto.es.js +12 -15
- package/dist/react-form-dto.umd.js +2 -2
- package/dist/stories/FormBuilder.stories.d.ts +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,41 +1,49 @@
|
|
|
1
1
|
# React Form DTO
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
**Schema-first, DTO-driven form builder for React and Material UI (MUI v7)**
|
|
4
|
+
|
|
5
|
+
React Form DTO helps you build complex, responsive, and accessible forms using declarative JSON/TypeScript DTOs instead of verbose JSX. It is designed for enterprise-grade applications, internal tools, admin panels, and workflows where forms are dynamic, configurable, and data-driven.
|
|
5
6
|
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
##
|
|
9
|
+
## Why React Form DTO?
|
|
9
10
|
|
|
10
|
-
-
|
|
11
|
-
- **Material UI integration**: Uses MUI v7 components for consistent design and accessibility.
|
|
12
|
-
- **Responsive grid layout**: 12-column system mapped to MUI’s `Grid` with `size` props.
|
|
13
|
-
- **Conditional rendering**: Show/hide fields or sections based on other field values.
|
|
14
|
-
- **Custom renderers**: Override default MUI inputs with your own components.
|
|
15
|
-
- **TypeScript support**: Strongly typed DTOs for safety and autocompletion.
|
|
16
|
-
- **Composable architecture**: `FormBuilder`, `Section`, and `Field` components.
|
|
17
|
-
- **Built-in validation**: Use `useFormBuilder` hook and validation utilities for field and form validation.
|
|
11
|
+
Most form libraries focus on low-level state management. React Form DTO operates at a **higher abstraction level**.
|
|
18
12
|
|
|
19
|
-
|
|
13
|
+
**Use this library when:**
|
|
20
14
|
|
|
21
|
-
|
|
15
|
+
- Your forms are generated from backend schemas or configurations
|
|
16
|
+
- You want to avoid duplicating UI logic across applications
|
|
17
|
+
- You need imperative control over form state (wizards, modals, async flows)
|
|
18
|
+
- Your project is built on Material UI
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
**Key advantages:**
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
- 📄 **DTO-first design** – define forms entirely in JSON or TypeScript
|
|
23
|
+
- 🎨 **Material UI v7 native** – consistent design & accessibility
|
|
24
|
+
- 🧱 **Composable architecture** – Form → Section → Field
|
|
25
|
+
- 🎯 **Imperative API** – programmatic access via refs
|
|
26
|
+
- 🔀 **Conditional rendering** – show/hide fields dynamically
|
|
27
|
+
- 🧩 **Custom renderers** – plug in your own components
|
|
28
|
+
- 🛡️ **Strong TypeScript typing** – predictable, safe APIs
|
|
26
29
|
|
|
27
30
|
---
|
|
28
31
|
|
|
29
|
-
##
|
|
32
|
+
## How It Compares
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
| Feature | React Form DTO | React Hook Form | Formik |
|
|
35
|
+
|------|---------------|----------------|--------|
|
|
36
|
+
| Schema/DTO driven | ✅ Native | ❌ Manual | ❌ Manual |
|
|
37
|
+
| MUI-first | ✅ Yes | ⚠️ Partial | ⚠️ Partial |
|
|
38
|
+
| Imperative API | ✅ First-class | ⚠️ Limited | ⚠️ Limited |
|
|
39
|
+
| Large dynamic forms | ✅ Excellent | ⚠️ Medium | ❌ Poor |
|
|
40
|
+
| Boilerplate | ✅ Minimal | ❌ High | ❌ High |
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
> React Form DTO is **not a replacement** for React Hook Form. It is a higher-level abstraction for schema-driven UI generation.
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
---
|
|
37
45
|
|
|
38
|
-
|
|
46
|
+
## Installation
|
|
39
47
|
|
|
40
48
|
```bash
|
|
41
49
|
git clone https://github.com/Shakir-Afridi/react-form-dto.git
|
|
@@ -43,16 +51,15 @@ cd react-form-dto
|
|
|
43
51
|
npm install
|
|
44
52
|
```
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
> - Node.js >= 18
|
|
48
|
-
> - React >= 19
|
|
49
|
-
> - Material UI >= 7
|
|
54
|
+
### Requirements
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
- Node.js >= 18
|
|
57
|
+
- React >= 19
|
|
58
|
+
- Material UI >= 7
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
---
|
|
54
61
|
|
|
55
|
-
|
|
62
|
+
## Minimal Example
|
|
56
63
|
|
|
57
64
|
```tsx
|
|
58
65
|
import { FormBuilder, type FormBuilderHandle } from 'react-form-dto';
|
|
@@ -79,11 +86,9 @@ return (
|
|
|
79
86
|
);
|
|
80
87
|
```
|
|
81
88
|
|
|
82
|
-
Refer to the documentation and examples for DTO structure and customization.
|
|
83
|
-
|
|
84
89
|
---
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
## 📋 Example Form rendered
|
|
87
92
|
|
|
88
93
|

|
|
89
94
|
|
|
@@ -187,28 +192,50 @@ const profileForm: FormDTO = {
|
|
|
187
192
|
|
|
188
193
|
---
|
|
189
194
|
|
|
190
|
-
|
|
195
|
+
## Supported Field Types
|
|
191
196
|
|
|
192
|
-
|
|
193
|
-
- **SelectInput**: select/dropdown
|
|
194
|
-
- **CheckBoxInput**: boolean/checkbox
|
|
195
|
-
- **AutoCompleteField**: single autocomplete
|
|
196
|
-
- **MultiAutoCompleteField**: multi-select autocomplete
|
|
197
|
+
### Text Inputs
|
|
197
198
|
|
|
198
|
-
|
|
199
|
+
- `text`
|
|
200
|
+
- `number`
|
|
201
|
+
- `date`
|
|
202
|
+
- `email`
|
|
203
|
+
- `password`
|
|
199
204
|
|
|
200
|
-
|
|
205
|
+
### Selection Inputs
|
|
206
|
+
|
|
207
|
+
- `select`
|
|
208
|
+
- `autocomplete`
|
|
209
|
+
- `multi-autocomplete`
|
|
210
|
+
|
|
211
|
+
### Boolean Inputs
|
|
212
|
+
|
|
213
|
+
- `checkbox`
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Custom Field Renderers
|
|
218
|
+
|
|
219
|
+
Override any default renderer by supplying your own component:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
201
222
|
{
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
id: "salary",
|
|
224
|
+
type: "number",
|
|
225
|
+
label: "Salary",
|
|
226
|
+
renderer: CurrencyInput
|
|
204
227
|
}
|
|
205
228
|
```
|
|
206
229
|
|
|
207
|
-
|
|
230
|
+
This makes the library extensible without modifying core logic.
|
|
231
|
+
|
|
232
|
+
---
|
|
208
233
|
|
|
209
|
-
|
|
234
|
+
## Validation API
|
|
210
235
|
|
|
211
|
-
|
|
236
|
+
Validation is handled through the `useFormBuilder` hook and the imperative form handle.
|
|
237
|
+
|
|
238
|
+
```ts
|
|
212
239
|
const {
|
|
213
240
|
handleChange, // Function to update a field value: (id, value) => void
|
|
214
241
|
validateAll, // Function to validate all fields: () => Record<string, string[]>
|
|
@@ -218,13 +245,42 @@ const {
|
|
|
218
245
|
} = useFormBuilder(myFormDTO);
|
|
219
246
|
```
|
|
220
247
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
248
|
+
### Validation Rules
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
validations: {
|
|
252
|
+
required: "This field is required",
|
|
253
|
+
validate: (value) => value.length < 2 ? "Minimum 2 characters" : null
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
> Recommendation: Standardize validation return values to `string[]` for predictable handling in large applications.
|
|
226
258
|
|
|
227
|
-
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Documentation & Demo
|
|
262
|
+
|
|
263
|
+
- 📘 **Documentation:** See full DTO reference, APIs, and advanced examples [Documentation](https://shakir-afridi.github.io/react-form-dto/docs)
|
|
264
|
+
- 📗 **Storybook:** Interactive component playground and live demos [Live Demo](https://shakir-afridi.github.io/react-form-dto/storybook)
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Ideal Use Cases
|
|
269
|
+
|
|
270
|
+
- Admin dashboards
|
|
271
|
+
- Internal enterprise tools
|
|
272
|
+
- Multi-step onboarding flows
|
|
273
|
+
- Config-driven forms from APIs
|
|
274
|
+
- Rapid UI scaffolding for MUI projects
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Roadmap (Suggested)
|
|
279
|
+
|
|
280
|
+
- Field registry API (`registerFieldType`)
|
|
281
|
+
- Async validation support
|
|
282
|
+
- Form-level conditional logic
|
|
283
|
+
- Schema import (JSON Schema / OpenAPI)
|
|
228
284
|
|
|
229
285
|
---
|
|
230
286
|
|
|
@@ -240,4 +296,8 @@ Refer to the documentation and examples for DTO structure and customization.
|
|
|
240
296
|
|
|
241
297
|
## 📜 License
|
|
242
298
|
|
|
243
|
-
MIT
|
|
299
|
+
MIT
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
**React Form DTO — Schema-first forms for Material UI**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import B, { useState as
|
|
2
|
-
import { FormControl as U, InputLabel as me, Select as de, MenuItem as pe, Typography as _, TextField as S, FormControlLabel as G, Checkbox as be, FormHelperText as J, Autocomplete as X, FormLabel as he, RadioGroup as fe, Radio as xe, Box as ve, Grid as
|
|
1
|
+
import B, { useState as Y, useImperativeHandle as ce } from "react";
|
|
2
|
+
import { FormControl as U, InputLabel as me, Select as de, MenuItem as pe, Typography as _, TextField as S, FormControlLabel as G, Checkbox as be, FormHelperText as J, Autocomplete as X, FormLabel as he, RadioGroup as fe, Radio as xe, Box as ve, Grid as q } from "@mui/material";
|
|
3
3
|
var y = { exports: {} }, g = {};
|
|
4
4
|
var W;
|
|
5
5
|
function ge() {
|
|
@@ -184,7 +184,7 @@ function Ee() {
|
|
|
184
184
|
var f = Object.keys(u).filter(function(ie) {
|
|
185
185
|
return ie !== "key";
|
|
186
186
|
});
|
|
187
|
-
m = 0 < f.length ? "{key: someKey, " + f.join(": ..., ") + ": ...}" : "{key: someKey}",
|
|
187
|
+
m = 0 < f.length ? "{key: someKey, " + f.join(": ..., ") + ": ...}" : "{key: someKey}", z[d + m] || (f = 0 < f.length ? "{" + f.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
188
188
|
`A props object containing a "key" prop is being spread into JSX:
|
|
189
189
|
let props = %s;
|
|
190
190
|
<%s {...props} />
|
|
@@ -195,7 +195,7 @@ React keys must be passed directly to JSX without using spread:
|
|
|
195
195
|
d,
|
|
196
196
|
f,
|
|
197
197
|
d
|
|
198
|
-
),
|
|
198
|
+
), z[d + m] = !0);
|
|
199
199
|
}
|
|
200
200
|
if (d = null, i !== void 0 && (o(i), d = "" + i), c(u) && (o(u.key), d = "" + u.key), "key" in u) {
|
|
201
201
|
i = {};
|
|
@@ -231,7 +231,7 @@ React keys must be passed directly to JSX without using spread:
|
|
|
231
231
|
var L, N = {}, I = R.react_stack_bottom_frame.bind(
|
|
232
232
|
R,
|
|
233
233
|
s
|
|
234
|
-
)(),
|
|
234
|
+
)(), D = w(a(s)), z = {};
|
|
235
235
|
E.Fragment = k, E.jsx = function(r, u, i) {
|
|
236
236
|
var m = 1e4 > $.recentlyCreatedOwnerStacks++;
|
|
237
237
|
return b(
|
|
@@ -240,7 +240,7 @@ React keys must be passed directly to JSX without using spread:
|
|
|
240
240
|
i,
|
|
241
241
|
!1,
|
|
242
242
|
m ? Error("react-stack-top-frame") : I,
|
|
243
|
-
m ? w(a(r)) :
|
|
243
|
+
m ? w(a(r)) : D
|
|
244
244
|
);
|
|
245
245
|
}, E.jsxs = function(r, u, i) {
|
|
246
246
|
var m = 1e4 > $.recentlyCreatedOwnerStacks++;
|
|
@@ -250,7 +250,7 @@ React keys must be passed directly to JSX without using spread:
|
|
|
250
250
|
i,
|
|
251
251
|
!0,
|
|
252
252
|
m ? Error("react-stack-top-frame") : I,
|
|
253
|
-
m ? w(a(r)) :
|
|
253
|
+
m ? w(a(r)) : D
|
|
254
254
|
);
|
|
255
255
|
};
|
|
256
256
|
})()), E;
|
|
@@ -299,7 +299,6 @@ function Re({
|
|
|
299
299
|
value: t || "",
|
|
300
300
|
name: e.id,
|
|
301
301
|
onChange: (n) => o(n.target.value),
|
|
302
|
-
required: e.required,
|
|
303
302
|
disabled: e.disabled,
|
|
304
303
|
type: e.type,
|
|
305
304
|
slotProps: {
|
|
@@ -356,7 +355,6 @@ const ye = ({
|
|
|
356
355
|
name: e.id,
|
|
357
356
|
label: e.label,
|
|
358
357
|
placeholder: e.placeholder,
|
|
359
|
-
required: e.required,
|
|
360
358
|
disabled: e.disabled,
|
|
361
359
|
error: !!a,
|
|
362
360
|
helperText: a
|
|
@@ -383,7 +381,6 @@ const ye = ({
|
|
|
383
381
|
name: e.id,
|
|
384
382
|
label: e.label,
|
|
385
383
|
placeholder: e.placeholder,
|
|
386
|
-
required: e.required,
|
|
387
384
|
disabled: e.disabled,
|
|
388
385
|
error: !!a,
|
|
389
386
|
helperText: a
|
|
@@ -589,8 +586,8 @@ const Fe = ({
|
|
|
589
586
|
children: e.description
|
|
590
587
|
}
|
|
591
588
|
),
|
|
592
|
-
/* @__PURE__ */ l.jsx(
|
|
593
|
-
|
|
589
|
+
/* @__PURE__ */ l.jsx(q, { container: !0, spacing: 2, children: e.fields.map((s) => /* @__PURE__ */ l.jsx(
|
|
590
|
+
q,
|
|
594
591
|
{
|
|
595
592
|
size: Oe(s.layout?.cols),
|
|
596
593
|
children: /* @__PURE__ */ l.jsx(
|
|
@@ -711,7 +708,7 @@ const Fe = ({
|
|
|
711
708
|
return c;
|
|
712
709
|
};
|
|
713
710
|
function Ce(e) {
|
|
714
|
-
const [t, o] =
|
|
711
|
+
const [t, o] = Y({}), [a, n] = Y({});
|
|
715
712
|
return {
|
|
716
713
|
/** Current form values for all fields */
|
|
717
714
|
values: t,
|
|
@@ -729,7 +726,7 @@ function Ce(e) {
|
|
|
729
726
|
validateField: (b) => H(e, t, b)
|
|
730
727
|
};
|
|
731
728
|
}
|
|
732
|
-
const
|
|
729
|
+
const De = B.forwardRef(({ dto: e, renderers: t }, o) => {
|
|
733
730
|
const {
|
|
734
731
|
values: a,
|
|
735
732
|
handleChange: n,
|
|
@@ -786,7 +783,7 @@ export {
|
|
|
786
783
|
ye as AutoCompleteField,
|
|
787
784
|
Te as CheckBoxInput,
|
|
788
785
|
we as Field,
|
|
789
|
-
|
|
786
|
+
De as FormBuilder,
|
|
790
787
|
Se as MultiAutoCompleteField,
|
|
791
788
|
Ae as RadioInput,
|
|
792
789
|
Fe as Section,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
(function(d,
|
|
1
|
+
(function(d,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("react"),require("@mui/material")):typeof define=="function"&&define.amd?define(["exports","react","@mui/material"],h):(d=typeof globalThis<"u"?globalThis:d||self,h(d.ReactFormDTO={},d.React,d.MaterialUI))})(this,(function(d,h,i){"use strict";var j={exports:{}},E={};var I;function re(){if(I)return E;I=1;var e=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function o(a,n,s){var m=null;if(s!==void 0&&(m=""+s),n.key!==void 0&&(m=""+n.key),"key"in n){s={};for(var f in n)f!=="key"&&(s[f]=n[f])}else s=n;return n=s.ref,{$$typeof:e,type:a,key:m,ref:n!==void 0?n:null,props:s}}return E.Fragment=r,E.jsx=o,E.jsxs=o,E}var T={};var L;function ne(){return L||(L=1,process.env.NODE_ENV!=="production"&&(function(){function e(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===ge?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case F:return"Fragment";case de:return"Profiler";case ce:return"StrictMode";case fe:return"Suspense";case he:return"SuspenseList";case ve:return"Activity"}if(typeof t=="object")switch(typeof t.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),t.$$typeof){case ie:return"Portal";case pe:return t.displayName||"Context";case me:return(t._context.displayName||"Context")+".Consumer";case be:var u=t.render;return t=t.displayName,t||(t=u.displayName||u.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case xe:return u=t.displayName||null,u!==null?u:e(t.type)||"Memo";case w:u=t._payload,t=t._init;try{return e(t(u))}catch{}}return null}function r(t){return""+t}function o(t){try{r(t);var u=!1}catch{u=!0}if(u){u=console;var c=u.error,p=typeof Symbol=="function"&&Symbol.toStringTag&&t[Symbol.toStringTag]||t.constructor.name||"Object";return c.call(u,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",p),r(t)}}function a(t){if(t===F)return"<>";if(typeof t=="object"&&t!==null&&t.$$typeof===w)return"<...>";try{var u=e(t);return u?"<"+u+">":"<...>"}catch{return"<...>"}}function n(){var t=O.A;return t===null?null:t.getOwner()}function s(){return Error("react-stack-top-frame")}function m(t){if(X.call(t,"key")){var u=Object.getOwnPropertyDescriptor(t,"key").get;if(u&&u.isReactWarning)return!1}return t.key!==void 0}function f(t,u){function c(){Z||(Z=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",u))}c.isReactWarning=!0,Object.defineProperty(t,"key",{get:c,configurable:!0})}function y(){var t=e(this.type);return Q[t]||(Q[t]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),t=this.props.ref,t!==void 0?t:null}function R(t,u,c,p,k,P){var b=c.ref;return t={$$typeof:H,type:t,key:u,props:c,_owner:p},(b!==void 0?b:null)!==null?Object.defineProperty(t,"ref",{enumerable:!1,get:y}):Object.defineProperty(t,"ref",{enumerable:!1,value:null}),t._store={},Object.defineProperty(t._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(t,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(t,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:k}),Object.defineProperty(t,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:P}),Object.freeze&&(Object.freeze(t.props),Object.freeze(t)),t}function x(t,u,c,p,k,P){var b=u.children;if(b!==void 0)if(p)if(Ee(b)){for(p=0;p<b.length;p++)_(b[p]);Object.freeze&&Object.freeze(b)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else _(b);if(X.call(u,"key")){b=e(t);var g=Object.keys(u).filter(function(Te){return Te!=="key"});p=0<g.length?"{key: someKey, "+g.join(": ..., ")+": ...}":"{key: someKey}",te[b+p]||(g=0<g.length?"{"+g.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
2
2
|
let props = %s;
|
|
3
3
|
<%s {...props} />
|
|
4
4
|
React keys must be passed directly to JSX without using spread:
|
|
5
5
|
let props = %s;
|
|
6
|
-
<%s key={someKey} {...props} />`,p,b,g,b),te[b+p]=!0)}if(b=null,c!==void 0&&(o(c),b=""+c),m(u)&&(o(u.key),b=""+u.key),"key"in u){c={};for(var $ in u)$!=="key"&&(c[$]=u[$])}else c=u;return b&&
|
|
6
|
+
<%s key={someKey} {...props} />`,p,b,g,b),te[b+p]=!0)}if(b=null,c!==void 0&&(o(c),b=""+c),m(u)&&(o(u.key),b=""+u.key),"key"in u){c={};for(var $ in u)$!=="key"&&(c[$]=u[$])}else c=u;return b&&f(c,typeof t=="function"?t.displayName||t.name||"Unknown":t),R(t,b,c,n(),k,P)}function _(t){v(t)?t._store&&(t._store.validated=1):typeof t=="object"&&t!==null&&t.$$typeof===w&&(t._payload.status==="fulfilled"?v(t._payload.value)&&t._payload.value._store&&(t._payload.value._store.validated=1):t._store&&(t._store.validated=1))}function v(t){return typeof t=="object"&&t!==null&&t.$$typeof===H}var S=h,H=Symbol.for("react.transitional.element"),ie=Symbol.for("react.portal"),F=Symbol.for("react.fragment"),ce=Symbol.for("react.strict_mode"),de=Symbol.for("react.profiler"),me=Symbol.for("react.consumer"),pe=Symbol.for("react.context"),be=Symbol.for("react.forward_ref"),fe=Symbol.for("react.suspense"),he=Symbol.for("react.suspense_list"),xe=Symbol.for("react.memo"),w=Symbol.for("react.lazy"),ve=Symbol.for("react.activity"),ge=Symbol.for("react.client.reference"),O=S.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,X=Object.prototype.hasOwnProperty,Ee=Array.isArray,C=console.createTask?console.createTask:function(){return null};S={react_stack_bottom_frame:function(t){return t()}};var Z,Q={},K=S.react_stack_bottom_frame.bind(S,s)(),ee=C(a(s)),te={};T.Fragment=F,T.jsx=function(t,u,c){var p=1e4>O.recentlyCreatedOwnerStacks++;return x(t,u,c,!1,p?Error("react-stack-top-frame"):K,p?C(a(t)):ee)},T.jsxs=function(t,u,c){var p=1e4>O.recentlyCreatedOwnerStacks++;return x(t,u,c,!0,p?Error("react-stack-top-frame"):K,p?C(a(t)):ee)}})()),T}var N;function ae(){return N||(N=1,process.env.NODE_ENV==="production"?j.exports=re():j.exports=ne()),j.exports}var l=ae();function D({field:e,value:r,onChange:o,error:a}){return l.jsxs(i.FormControl,{fullWidth:!0,children:[l.jsx(i.InputLabel,{id:`${e.id}-label`,children:e.label}),l.jsx(i.Select,{labelId:`${e.id}-label`,value:r,id:e.id,name:e.id,onChange:n=>o(n.target.value),label:e.label,error:!!a,children:e.options?.map(n=>l.jsx(i.MenuItem,{value:n,children:n},n))}),a&&l.jsx(i.Typography,{variant:"caption",color:"error",children:a})]})}function z({field:e,value:r,onChange:o,error:a}){return console.log("error",a),l.jsx(i.TextField,{fullWidth:!0,label:e.label,placeholder:e.placeholder,value:r||"",name:e.id,onChange:n=>o(n.target.value),disabled:e.disabled,type:e.type,slotProps:{inputLabel:{shrink:e.type==="date"?!0:void 0}},error:!!a,helperText:a})}function Y({field:e,value:r,onChange:o,error:a}){return l.jsxs(l.Fragment,{children:[l.jsx(i.FormControlLabel,{name:e.id,control:l.jsx(i.Checkbox,{checked:!!r,onChange:n=>o(n.target.checked),disabled:e.disabled}),label:e.label}),a&&l.jsx(i.FormHelperText,{children:a})]})}const W=({field:e,value:r,onChange:o,error:a})=>l.jsx(i.Autocomplete,{fullWidth:!0,options:e.options||[],value:r||null,onChange:(n,s)=>o(s),renderInput:n=>l.jsx(i.TextField,{...n,name:e.id,label:e.label,placeholder:e.placeholder,disabled:e.disabled,error:!!a,helperText:a})}),M=({field:e,value:r,onChange:o,error:a})=>l.jsx(i.Autocomplete,{multiple:!0,fullWidth:!0,options:e.options||[],value:r||[],onChange:(n,s)=>o(s),renderInput:n=>l.jsx(i.TextField,{...n,name:e.id,label:e.label,placeholder:e.placeholder,disabled:e.disabled,error:!!a,helperText:a})}),V=({field:e,value:r,onChange:o,error:a})=>l.jsx(i.TextField,{fullWidth:!0,multiline:!0,rows:e.rows||4,label:e.label,placeholder:e.placeholder,value:r||"",onChange:n=>o(n.target.value),required:!!e.validations?.required,disabled:e.disabled,error:!!a,helperText:a}),q=({field:e,value:r,onChange:o,error:a})=>l.jsxs(i.FormControl,{component:"fieldset",fullWidth:!0,error:!!a,children:[l.jsxs(i.FormLabel,{component:"legend",children:[e.label,e.validations?.required?" *":""]}),l.jsx(i.RadioGroup,{row:e.layout?.direction==="row"||!1,value:r||"",onChange:n=>o(n.target.value),children:(e.options||[]).map(n=>l.jsx(i.FormControlLabel,{value:n,control:l.jsx(i.Radio,{}),label:n,disabled:e.disabled},n))}),a&&l.jsx(i.FormHelperText,{children:a})]}),oe=({field:e,value:r,onChange:o,error:a})=>{switch(e.type){case"text":case"date":case"email":case"password":case"number":return l.jsx(z,{field:e,value:r,onChange:o,error:a});case"select":return l.jsx(D,{field:e,value:r,onChange:o,error:a});case"autocomplete":return l.jsx(W,{field:e,value:r,onChange:o,error:a});case"multi-autocomplete":return l.jsx(M,{field:e,value:r,onChange:o,error:a});case"checkbox":return l.jsx(Y,{field:e,value:r,onChange:o,error:a});case"textarea":return l.jsx(V,{field:e,value:r,onChange:o,error:a});case"radio":return l.jsx(q,{field:e,value:r,onChange:o,error:a});default:return l.jsxs("span",{children:["Unsupported field type: ",e.type]})}},B=({field:e,value:r,onChange:o,error:a,renderers:n={}})=>{const s=n[e.type]||oe;return l.jsx(s,{field:e,value:r,onChange:o,error:a})};function se(e){const r=e??12;return{xs:12,sm:r,md:r,lg:r,xl:r}}const U=({section:e,values:r,onChange:o,renderers:a,validateField:n})=>l.jsxs(i.Box,{mb:2,children:[e.heading&&l.jsx(i.Typography,{variant:"h6",sx:{fontSize:e.headingFontSize?`${e.headingFontSize}rem`:"1.25rem"},gutterBottom:!0,color:"black",children:e.heading}),e.description&&l.jsx(i.Typography,{variant:"body2",sx:{fontSize:e.descriptionFontSize?`${e.descriptionFontSize}rem`:"inherit"},color:"textSecondary",gutterBottom:!0,children:e.description}),l.jsx(i.Grid,{container:!0,spacing:2,children:e.fields.map(s=>l.jsx(i.Grid,{size:se(s.layout?.cols),children:l.jsx(B,{field:s,value:r[s.id],onChange:m=>o(s.id,m),renderers:a,error:n(s.id)?.join(",")})},s.id))})]}),le={required:(e,r)=>e.required&&(r==null||r==="")?`${e.label} is required`:null,min:(e,r)=>e.type==="number"&&e.min!==void 0&&r<e.min?`${e.label} must be at least ${e.min}`:null,max:(e,r)=>e.type==="number"&&e.max!==void 0&&r>e.max?`${e.label} must be at most ${e.max}`:null,minLength:(e,r)=>typeof r=="string"&&e.minLength!==void 0&&r.length<e.minLength?`${e.label} must be at least ${e.minLength} characters`:null,maxLength:(e,r)=>typeof r=="string"&&e.maxLength!==void 0&&r.length>e.maxLength?`${e.label} must be at most ${e.maxLength} characters`:null,pattern:(e,r)=>e.pattern&&typeof r=="string"&&!e.pattern.test(r)?`${e.label} is invalid`:null,options:(e,r)=>e.type==="select"&&e.options&&!e.options.includes(r)?`${e.label} must be one of: ${e.options.join(", ")}`:null,dateRange:(e,r)=>{if(e.type==="date"&&r){const o=new Date(r);if(e.minDate&&o<new Date(e.minDate))return`${e.label} must be after ${e.minDate}`;if(e.maxDate&&o>new Date(e.maxDate))return`${e.label} must be before ${e.maxDate}`}return null},customValidator:(e,r)=>{if(e.customValidator&&typeof e.customValidator=="function"){const o=e.customValidator(r);if(typeof o=="string")return o}return null}},G=(e,r)=>{const o={};return e.sections.forEach(a=>{a.fields.forEach(n=>{const s=A(e,r,n.id);s.length>0&&(o[n.id]=s)})}),o},A=(e,r,o)=>{const a=e.sections.flatMap(f=>f.fields).find(f=>f.id===o);if(!a)return[];const n=a.validations||{},s=r[o],m=[];if(n.required&&(s==null||s==="")&&m.push(typeof n.required=="string"?n.required:`${a.label} is required`),n.min!==void 0&&typeof s=="number"&&s<n.min&&m.push(`${a.label} must be at least ${n.min}`),n.max!==void 0&&typeof s=="number"&&s>n.max&&m.push(`${a.label} must be at most ${n.max}`),n.minLength!==void 0&&typeof s=="string"&&s.length<n.minLength&&m.push(`${a.label} must be at least ${n.minLength} characters`),n.maxLength!==void 0&&typeof s=="string"&&s.length>n.maxLength&&m.push(`${a.label} must be at most ${n.maxLength} characters`),n.pattern&&typeof s=="string"&&!n.pattern.test(s)&&m.push(`${a.label} is invalid`),n.validate){const f=n.validate(s);f&&m.push(f)}return m};function J(e){const[r,o]=h.useState({}),[a,n]=h.useState({});return{values:r,handleChange:(x,_)=>{o(v=>({...v,[x]:_})),n(v=>({...v,[x]:null}))},validateAll:()=>G(e,r),getValues:()=>r,getErrors:()=>a,validateField:x=>A(e,r,x)}}const ue=h.forwardRef(({dto:e,renderers:r},o)=>{const{values:a,handleChange:n,getValues:s,getErrors:m,validateAll:f,validateField:y}=J(e);return h.useImperativeHandle(o,()=>({getValues:s,getErrors:m,validateAll:f,validateField:y})),l.jsxs(l.Fragment,{children:[e.title&&l.jsx(i.Typography,{variant:"h5",color:"black",sx:{fontSize:e.titleFontSize?`${e.titleFontSize}rem`:"1.5rem",fontWeight:"bold"},gutterBottom:!0,children:e.title}),e.description&&l.jsx(i.Typography,{component:"p",sx:{fontSize:e.descriptionFontSize?`${e.descriptionFontSize}rem`:"inherit"},color:"textSecondary",gutterBottom:!0,children:e.description}),e.sections.map(R=>l.jsx(U,{section:R,values:a,onChange:n,renderers:r,validateField:y},R.id))]})});d.AutoCompleteField=W,d.CheckBoxInput=Y,d.Field=B,d.FormBuilder=ue,d.MultiAutoCompleteField=M,d.RadioInput=q,d.Section=U,d.SelectInput=D,d.TextAreaInput=V,d.TextInput=z,d.useFormBuilder=J,d.validateAll=G,d.validateField=A,d.validationRules=le,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-form-dto",
|
|
3
3
|
"description": "A React library for building forms using DTOs with MUI and TypeScript.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.6",
|
|
5
5
|
"main": "dist/react-form-dto.umd.js",
|
|
6
6
|
"module": "dist/react-form-dto.es.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@emotion/react": "^11.14.0",
|
|
56
56
|
"@emotion/styled": "^11.14.1",
|
|
57
|
+
"@mui/icons-material": "^7.3.6",
|
|
57
58
|
"@storybook/react": "^10.1.4",
|
|
58
59
|
"@storybook/react-vite": "^10.1.4",
|
|
59
60
|
"storybook": "^10.1.4"
|