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 CHANGED
@@ -1,41 +1,49 @@
1
1
  # React Form DTO
2
2
 
3
- A **dynamic, DTO-driven form builder** built with **React, TypeScript, and Material UI (MUI v7)**.
4
- This library lets you define forms declaratively using JSON DTOs (`FormDTO`, `SectionDTO`, `FieldDTO`) and automatically renders responsive, accessible forms with MUI components.
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
- ## Features
9
+ ## Why React Form DTO?
9
10
 
10
- - **DTO-driven**: Define forms entirely in JSON/TypeScript objects.
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
- 📘 Documentation
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
- Comprehensive documentation is available here
20
+ **Key advantages:**
24
21
 
25
- 👉 [Documentation](https://shakir-afridi.github.io/react-form-dto/docs)
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
- ## 📘 Storybook
32
+ ## How It Compares
30
33
 
31
- Explore it interactively on Storybook:
32
- 👉 [Live Demo](https://shakir-afridi.github.io/react-form-dto/storybook)
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
- ## 📦 Installation
44
+ ---
37
45
 
38
- Clone the repo and install dependencies:
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
- > **Requirements:**
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
- ## 🏗️ Usage
60
+ ---
54
61
 
55
- Import and use the form builder in your React app:
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
- ### 📋 Example
91
+ ## 📋 Example Form rendered
87
92
 
88
93
  ![Form Example](./example.png)
89
94
 
@@ -187,28 +192,50 @@ const profileForm: FormDTO = {
187
192
 
188
193
  ---
189
194
 
190
- ### Supported Field Types & Renderers
195
+ ## Supported Field Types
191
196
 
192
- - **TextInput**: text, number, date
193
- - **SelectInput**: select/dropdown
194
- - **CheckBoxInput**: boolean/checkbox
195
- - **AutoCompleteField**: single autocomplete
196
- - **MultiAutoCompleteField**: multi-select autocomplete
197
+ ### Text Inputs
197
198
 
198
- You can provide a custom renderer for any field via the DTO:
199
+ - `text`
200
+ - `number`
201
+ - `date`
202
+ - `email`
203
+ - `password`
199
204
 
200
- ```tsx
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
- // ...existing field DTO...
203
- renderer: MyCustomComponent
223
+ id: "salary",
224
+ type: "number",
225
+ label: "Salary",
226
+ renderer: CurrencyInput
204
227
  }
205
228
  ```
206
229
 
207
- ### Validation
230
+ This makes the library extensible without modifying core logic.
231
+
232
+ ---
208
233
 
209
- The `useFormBuilder` hook provides the following API for managing form state and validation:
234
+ ## Validation API
210
235
 
211
- ```tsx
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
- - `handleChange(id, value)`: Update a field value.
222
- - `validateAll()`: Validate all fields and return errors.
223
- - `validateField(id)`: Validate a specific field and return errors for that field.
224
- - `getValues()`: Get all form values.
225
- - `getErrors()`: Get all form errors.
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
- Refer to the documentation and examples for DTO structure and customization.
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 License © 2025 Shakir Ullah
299
+ MIT
300
+
301
+ ---
302
+
303
+ **React Form DTO — Schema-first forms for Material UI**
@@ -1,5 +1,5 @@
1
- import B, { useState as z, 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 Y } from "@mui/material";
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}", D[d + m] || (f = 0 < f.length ? "{" + f.join(": ..., ") + ": ...}" : "{}", console.error(
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
- ), D[d + m] = !0);
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
- )(), q = w(a(s)), D = {};
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)) : q
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)) : q
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(Y, { container: !0, spacing: 2, children: e.fields.map((s) => /* @__PURE__ */ l.jsx(
593
- Y,
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] = z({}), [a, n] = z({});
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 qe = B.forwardRef(({ dto: e, renderers: t }, o) => {
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
- qe as FormBuilder,
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,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("react"),require("@mui/material")):typeof define=="function"&&define.amd?define(["exports","react","@mui/material"],f):(d=typeof globalThis<"u"?globalThis:d||self,f(d.ReactFormDTO={},d.React,d.MaterialUI))})(this,(function(d,f,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 h in n)h!=="key"&&(s[h]=n[h])}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 he:return"Suspense";case fe: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 h(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:
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&&h(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=f,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"),he=Symbol.for("react.suspense"),fe=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 q({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),required:e.required,disabled:e.disabled,type:e.type,slotProps:{inputLabel:{shrink:e.type==="date"?!0:void 0}},error:!!a,helperText:a})}function z({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 Y=({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,required:e.required,disabled:e.disabled,error:!!a,helperText:a})}),W=({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,required:e.required,disabled:e.disabled,error:!!a,helperText:a})}),M=({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}),V=({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(q,{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(Y,{field:e,value:r,onChange:o,error:a});case"multi-autocomplete":return l.jsx(W,{field:e,value:r,onChange:o,error:a});case"checkbox":return l.jsx(z,{field:e,value:r,onChange:o,error:a});case"textarea":return l.jsx(M,{field:e,value:r,onChange:o,error:a});case"radio":return l.jsx(V,{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(h=>h.fields).find(h=>h.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 h=n.validate(s);h&&m.push(h)}return m};function J(e){const[r,o]=f.useState({}),[a,n]=f.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=f.forwardRef(({dto:e,renderers:r},o)=>{const{values:a,handleChange:n,getValues:s,getErrors:m,validateAll:h,validateField:y}=J(e);return f.useImperativeHandle(o,()=>({getValues:s,getErrors:m,validateAll:h,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=Y,d.CheckBoxInput=z,d.Field=B,d.FormBuilder=ue,d.MultiAutoCompleteField=W,d.RadioInput=V,d.Section=U,d.SelectInput=D,d.TextAreaInput=M,d.TextInput=q,d.useFormBuilder=J,d.validateAll=G,d.validateField=A,d.validationRules=le,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
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"})}));
@@ -15,6 +15,7 @@ declare const _default: {
15
15
  };
16
16
  };
17
17
  export default _default;
18
+ export declare const Playground: () => import("react/jsx-runtime").JSX.Element;
18
19
  export declare const EditableDTO: {
19
20
  (args: {
20
21
  dto: FormDTO;
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",
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"