react-form-dto 1.0.3 → 1.0.4
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
|
@@ -146,6 +146,148 @@ return (
|
|
|
146
146
|
|
|
147
147
|
---
|
|
148
148
|
|
|
149
|
+
## useFormBuilderController – Controlled, Auto‑Rendered Forms
|
|
150
|
+
|
|
151
|
+
`useFormBuilderController` is a hook that wraps `FormBuilder` and gives you:
|
|
152
|
+
|
|
153
|
+
- An auto-rendering `Form` component (no manual mapping of sections/fields)
|
|
154
|
+
- An imperative controller API for values, errors, and validation
|
|
155
|
+
|
|
156
|
+
It combines the convenience of `FormBuilder` with the programmatic control of `useFormBuilder`.
|
|
157
|
+
|
|
158
|
+
### When to Use
|
|
159
|
+
|
|
160
|
+
- You want the form UI to be generated entirely from a DTO
|
|
161
|
+
- You need to:
|
|
162
|
+
- Run validation on submit
|
|
163
|
+
- Read values/errors from outside the form
|
|
164
|
+
- Prefill fields programmatically
|
|
165
|
+
- Plug in custom field renderers
|
|
166
|
+
|
|
167
|
+
### Basic Example
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import { useFormBuilderController } from 'react-form-dto';
|
|
171
|
+
import { myFormDTO } from './myFormDTO';
|
|
172
|
+
|
|
173
|
+
function MyFormWithController() {
|
|
174
|
+
const formController = useFormBuilderController({
|
|
175
|
+
dto: myFormDTO,
|
|
176
|
+
locale: 'en',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
|
|
182
|
+
const errors = formController.validateAll();
|
|
183
|
+
const hasErrors = Object.values(errors).some(
|
|
184
|
+
(err) => err && err.length > 0,
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if (hasErrors) {
|
|
188
|
+
console.log('Validation errors:', errors);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log('Form submitted:', formController.getValues());
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<form onSubmit={handleSubmit}>
|
|
197
|
+
{/* Auto-renders the full form from the DTO */}
|
|
198
|
+
<formController.Form />
|
|
199
|
+
<button type="submit">Submit</button>
|
|
200
|
+
</form>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### API Overview
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const controller = useFormBuilderController({
|
|
209
|
+
dto: myFormDTO,
|
|
210
|
+
locale: 'en',
|
|
211
|
+
renderers: {
|
|
212
|
+
// optional: override field types
|
|
213
|
+
text: MyTextField,
|
|
214
|
+
},
|
|
215
|
+
handleChangeCallback: (id, value) => {
|
|
216
|
+
// optional: side effects on every field change
|
|
217
|
+
console.log(id, value);
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Reading and validating
|
|
222
|
+
controller.getValues(); // Record<string, any>
|
|
223
|
+
controller.getErrors(); // Record<string, string | null>
|
|
224
|
+
controller.validateAll(); // Record<string, string[] | null>
|
|
225
|
+
controller.validateField('id'); // string[]
|
|
226
|
+
|
|
227
|
+
// Programmatic updates
|
|
228
|
+
controller.handleChange('firstName', 'Jane');
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Example with Prefill and Custom Renderers
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { useFormBuilderController } from 'react-form-dto';
|
|
235
|
+
import { myFormDTO } from './myFormDTO';
|
|
236
|
+
import { TextInput, SelectInput } from './customFields';
|
|
237
|
+
|
|
238
|
+
const renderers = {
|
|
239
|
+
text: TextInput,
|
|
240
|
+
select: SelectInput,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
function AdvancedForm() {
|
|
244
|
+
const formController = useFormBuilderController({
|
|
245
|
+
dto: myFormDTO,
|
|
246
|
+
locale: 'en',
|
|
247
|
+
renderers,
|
|
248
|
+
handleChangeCallback: (id, value) => {
|
|
249
|
+
// sync with external store / analytics, etc.
|
|
250
|
+
console.log('Changed:', id, value);
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const prefill = () => {
|
|
255
|
+
formController.handleChange('firstName', 'John');
|
|
256
|
+
formController.handleChange('lastName', 'Doe');
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<>
|
|
261
|
+
<button type="button" onClick={prefill}>
|
|
262
|
+
Prefill
|
|
263
|
+
</button>
|
|
264
|
+
|
|
265
|
+
<form
|
|
266
|
+
onSubmit={(e) => {
|
|
267
|
+
e.preventDefault();
|
|
268
|
+
const errors = formController.validateAll();
|
|
269
|
+
const hasErrors = Object.values(errors).some(
|
|
270
|
+
(err) => err && err.length > 0,
|
|
271
|
+
);
|
|
272
|
+
if (!hasErrors) {
|
|
273
|
+
console.log('Values:', formController.getValues());
|
|
274
|
+
}
|
|
275
|
+
}}
|
|
276
|
+
>
|
|
277
|
+
<formController.Form />
|
|
278
|
+
<button type="submit">Submit</button>
|
|
279
|
+
</form>
|
|
280
|
+
</>
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
> For the full hook reference, see
|
|
286
|
+
> **Docs → Hooks → `useFormBuilderController`**:
|
|
287
|
+
> `docs/api/hooks/useFormBuilderController.md`
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
149
291
|
## 📋 Example Form rendered
|
|
150
292
|
|
|
151
293
|

|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FormDTO } from '../types';
|
|
2
|
+
type FormBuilderControllerProps = {
|
|
3
|
+
dto: FormDTO;
|
|
4
|
+
locale: string;
|
|
5
|
+
renderers?: Record<string, React.ComponentType<any>>;
|
|
6
|
+
handleChangeCallback?: (id: string, val: any) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare const useFormBuilderController: (props: FormBuilderControllerProps) => {
|
|
9
|
+
getValues: () => Record<string, any>;
|
|
10
|
+
getErrors: () => Record<string, string | null>;
|
|
11
|
+
validateAll: () => Record<string, string[]>;
|
|
12
|
+
validateField: (id: string) => string[];
|
|
13
|
+
handleChange: (id: string, val: any) => void | undefined;
|
|
14
|
+
Form: () => import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -804,7 +804,8 @@ const Ye = X.forwardRef(({ dto: e, renderers: t, locale: n = "en", handleChangeC
|
|
|
804
804
|
getValues: c,
|
|
805
805
|
getErrors: h,
|
|
806
806
|
validateAll: x,
|
|
807
|
-
validateField: E
|
|
807
|
+
validateField: E,
|
|
808
|
+
handleChange: u
|
|
808
809
|
})), /* @__PURE__ */ l.jsxs(l.Fragment, { children: [
|
|
809
810
|
e.title && /* @__PURE__ */ l.jsx(
|
|
810
811
|
T,
|
|
@@ -3,4 +3,4 @@
|
|
|
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} />`,b,h,_,h),se[h+b]=!0)}if(h=null,p!==void 0&&(n(p),h=""+p),u(i)&&(n(i.key),h=""+i.key),"key"in i){p={};for(var z in i)z!=="key"&&(p[z]=i[z])}else p=i;return h&&d(p,typeof r=="function"?r.displayName||r.name||"Unknown":r),g(r,h,p,o(),O,D)}function R(r){T(r)?r._store&&(r._store.validated=1):typeof r=="object"&&r!==null&&r.$$typeof===I&&(r._payload.status==="fulfilled"?T(r._payload.value)&&r._payload.value._store&&(r._payload.value._store.validated=1):r._store&&(r._store.validated=1))}function T(r){return typeof r=="object"&&r!==null&&r.$$typeof===y}var v=E,y=Symbol.for("react.transitional.element"),C=Symbol.for("react.portal"),$=Symbol.for("react.fragment"),me=Symbol.for("react.strict_mode"),fe=Symbol.for("react.profiler"),be=Symbol.for("react.consumer"),he=Symbol.for("react.context"),ve=Symbol.for("react.forward_ref"),xe=Symbol.for("react.suspense"),ge=Symbol.for("react.suspense_list"),ye=Symbol.for("react.memo"),I=Symbol.for("react.lazy"),Ee=Symbol.for("react.activity"),Te=Symbol.for("react.client.reference"),L=v.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ee=Object.prototype.hasOwnProperty,je=Array.isArray,N=console.createTask?console.createTask:function(){return null};v={react_stack_bottom_frame:function(r){return r()}};var re,te={},ne=v.react_stack_bottom_frame.bind(v,s)(),ae=N(a(s)),se={};S.Fragment=$,S.jsx=function(r,i,p){var b=1e4>L.recentlyCreatedOwnerStacks++;return j(r,i,p,!1,b?Error("react-stack-top-frame"):ne,b?N(a(r)):ae)},S.jsxs=function(r,i,p){var b=1e4>L.recentlyCreatedOwnerStacks++;return j(r,i,p,!0,b?Error("react-stack-top-frame"):ne,b?N(a(r)):ae)}})()),S}var W;function le(){return W||(W=1,process.env.NODE_ENV==="production"?k.exports=oe():k.exports=ue()),k.exports}var l=le();function f(e,t="en"){return e?typeof e=="string"?e:e[t]||e.en||Object.values(e)[0]||"":""}function F(e,t="en"){return e?e.map(n=>typeof n=="string"?{value:n,label:n}:{value:n.value,label:f(n.label,t)}):[]}function M({field:e,value:t,onChange:n,error:a,locale:o="en"}){const s=F(e.options||[],o);return l.jsxs(c.FormControl,{fullWidth:!0,children:[l.jsx(c.InputLabel,{id:`${e.id}-label`,children:f(e.label,o)}),l.jsx(c.Select,{labelId:`${e.id}-label`,value:t,id:e.id,name:e.id,onChange:u=>n(u.target.value),label:f(e.label,o),error:!!a,children:s?.map(u=>l.jsx(c.MenuItem,{value:u.value,children:u.label},u.label))}),a&&l.jsx(c.Typography,{variant:"caption",color:"error",children:a})]})}function V({field:e,value:t,onChange:n,error:a,locale:o}){return console.log("error",a),l.jsx(c.TextField,{fullWidth:!0,label:f(e.label,o),placeholder:f(e.placeholder||"",o),value:t||"",name:e.id,onChange:s=>n(s.target.value),disabled:e.disabled,type:e.type,slotProps:{inputLabel:{shrink:e.type==="date"?!0:void 0}},error:!!a,helperText:a})}function B({field:e,value:t,onChange:n,error:a,locale:o}){return l.jsxs(l.Fragment,{children:[l.jsx(c.FormControlLabel,{name:e.id,control:l.jsx(c.Checkbox,{checked:!!t,onChange:s=>n(s.target.checked),disabled:e.disabled}),label:f(e.label,o)}),a&&l.jsx(c.FormHelperText,{children:a})]})}const U=({field:e,value:t,onChange:n,error:a,locale:o})=>{const s=F(e.options||[],o);return l.jsx(c.Autocomplete,{fullWidth:!0,options:s||[],value:t||null,onChange:(u,d)=>n(d),renderInput:u=>l.jsx(c.TextField,{...u,name:e.id,label:f(e.label,o),placeholder:f(e.placeholder||"",o),disabled:e.disabled,error:!!a,helperText:a})})},G=({field:e,value:t,onChange:n,error:a,locale:o})=>{const s=F(e.options||[],o);return l.jsx(c.Autocomplete,{multiple:!0,fullWidth:!0,options:s||[],value:t||[],onChange:(u,d)=>n(d),renderInput:u=>l.jsx(c.TextField,{...u,name:e.id,label:f(e.label,o),placeholder:f(e.placeholder||"",o),disabled:e.disabled,error:!!a,helperText:a})})},J=({field:e,value:t,onChange:n,error:a,locale:o="en"})=>l.jsx(c.TextField,{fullWidth:!0,multiline:!0,rows:e.rows||4,label:f(e.label,o),placeholder:f(e.placeholder||"",o),value:t||"",onChange:s=>n(s.target.value),required:!!e.validations?.required,disabled:e.disabled,error:!!a,helperText:a}),H=({field:e,value:t,onChange:n,error:a,locale:o="en"})=>{const s=F(e.options||[],o);return l.jsxs(c.FormControl,{component:"fieldset",fullWidth:!0,error:!!a,children:[l.jsxs(c.FormLabel,{component:"legend",children:[f(e.label,o),e.validations?.required?" *":""]}),l.jsx(c.RadioGroup,{row:e.layout?.direction==="row"||!1,value:t||"",onChange:u=>n(u.target.value),children:(s||[]).map(u=>l.jsx(c.FormControlLabel,{value:u.value,control:l.jsx(c.Radio,{}),label:u.label,disabled:e.disabled},u.label))}),a&&l.jsx(c.FormHelperText,{children:a})]})},ie=({field:e,value:t,onChange:n,error:a,locale:o="en"})=>{switch(e.type){case"text":case"date":case"email":case"password":case"number":return l.jsx(V,{field:e,value:t,onChange:n,error:a,locale:o});case"select":return l.jsx(M,{field:e,value:t,onChange:n,error:a,locale:o});case"autocomplete":return l.jsx(U,{field:e,value:t,onChange:n,error:a,locale:o});case"multi-autocomplete":return l.jsx(G,{field:e,value:t,onChange:n,error:a,locale:o});case"checkbox":return l.jsx(B,{field:e,value:t,onChange:n,error:a,locale:o});case"textarea":return l.jsx(J,{field:e,value:t,onChange:n,error:a,locale:o});case"radio":return l.jsx(H,{field:e,value:t,onChange:n,error:a,locale:o});default:return l.jsxs("span",{children:["Unsupported field type: ",e.type]})}},X=({field:e,value:t,onChange:n,error:a,renderers:o={},locale:s="en"})=>{const u=o[e.type]||ie;return l.jsx(u,{field:e,value:t,onChange:n,error:a,locale:s})};function ce(e){const t=e??12;return{xs:12,sm:t,md:t,lg:t,xl:t}}const de={required:(e,t)=>e.required&&(t==null||t==="")?`${e.label} is required`:null,min:(e,t)=>e.type==="number"&&e.min!==void 0&&t<e.min?`${e.label} must be at least ${e.min}`:null,max:(e,t)=>e.type==="number"&&e.max!==void 0&&t>e.max?`${e.label} must be at most ${e.max}`:null,minLength:(e,t)=>typeof t=="string"&&e.minLength!==void 0&&t.length<e.minLength?`${e.label} must be at least ${e.minLength} characters`:null,maxLength:(e,t)=>typeof t=="string"&&e.maxLength!==void 0&&t.length>e.maxLength?`${e.label} must be at most ${e.maxLength} characters`:null,pattern:(e,t)=>e.pattern&&typeof t=="string"&&!e.pattern.test(t)?`${e.label} is invalid`:null,options:(e,t)=>e.type==="select"&&e.options&&!e.options.includes(t)?`${e.label} must be one of: ${e.options.join(", ")}`:null,dateRange:(e,t)=>{if(e.type==="date"&&t){const n=new Date(t);if(e.minDate&&n<new Date(e.minDate))return`${e.label} must be after ${e.minDate}`;if(e.maxDate&&n>new Date(e.maxDate))return`${e.label} must be before ${e.maxDate}`}return null},customValidator:(e,t)=>{if(e.customValidator&&typeof e.customValidator=="function"){const n=e.customValidator(t);if(typeof n=="string")return n}return null}},Z=(e,t,n="en")=>{const a={};return e.sections.forEach(o=>{o.fields.forEach(s=>{const u=w(e,t,s.id);u.length>0&&(a[s.id]=u)})}),a},w=(e,t,n,a="en")=>{const o=e.sections.flatMap(g=>g.fields).find(g=>g.id===n);if(!o)return[];const s=o.validations||{},u=t[n],d=[],x=f(o.label,a);if(s.required&&(u==null||u==="")&&d.push(typeof s.required=="string"?s.required:`${x} is required`),s.min!==void 0&&typeof u=="number"&&u<s.min&&d.push(`${x} must be at least ${s.min}`),s.max!==void 0&&typeof u=="number"&&u>s.max&&d.push(`${x} must be at most ${s.max}`),s.minLength!==void 0&&typeof u=="string"&&u.length<s.minLength&&d.push(`${x} must be at least ${s.minLength} characters`),s.maxLength!==void 0&&typeof u=="string"&&u.length>s.maxLength&&d.push(`${x} must be at most ${s.maxLength} characters`),s.pattern&&typeof u=="string"&&!s.pattern.test(u)&&d.push(`${x} is invalid`),s.validate){const g=s.validate(u);g&&d.push(f(g,a))}return d};function P(e,t){if(!e)return!0;if("field"in e){let n=t[e.field]??"";return Array.isArray(n)?n=n.map(a=>a.value??""):typeof n=="object"&&n!==null&&(n=n.value??""),e.equals!==void 0?n===e.equals:e.notEquals!==void 0?n!==e.notEquals:e.in!==void 0?Array.isArray(n)?e.in?n.some(a=>e.in.includes(a)):!1:e.in.includes(n):e.notIn!==void 0?Array.isArray(n)?n.every(a=>!e.notIn.includes(a)):!e.notIn.includes(n):e.greaterThan!==void 0?n>e.greaterThan:e.lessThan!==void 0?n<e.lessThan:!0}if("operator"in e){if(e.operator==="AND")return e.conditions.every(a=>P(a,t));if(e.operator==="OR")return e.conditions.some(n=>P(n,t))}return!1}const Q=({section:e,values:t,onChange:n,renderers:a,validateField:o,locale:s="en"})=>l.jsxs(c.Box,{mb:2,children:[e.heading&&l.jsx(c.Typography,{variant:"h6",sx:{fontSize:e.headingFontSize?`${e.headingFontSize}rem`:"1.25rem"},gutterBottom:!0,color:"black",children:f(e.heading,s)}),e.description&&l.jsx(c.Typography,{variant:"body2",sx:{fontSize:e.descriptionFontSize?`${e.descriptionFontSize}rem`:"inherit"},color:"textSecondary",gutterBottom:!0,children:f(e.description,s)}),l.jsx(c.Grid,{container:!0,spacing:2,children:e.fields.map(u=>P(u.visibleWhen,t)&&l.jsx(c.Grid,{size:ce(u.layout?.cols),children:l.jsx(X,{field:u,value:t[u.id],onChange:d=>n(u.id,d),renderers:a,error:o(u.id)?.join(","),locale:s})},u.id))})]});function K(e,t="en",n){const[a,o]=E.useState({}),[s,u]=E.useState({});return{values:a,handleChange:(T,v)=>{if(o(y=>({...y,[T]:v})),u(y=>({...y,[T]:null})),n){let y=v;Array.isArray(v)?y=v.map(C=>C.value??""):typeof v=="object"&&v!==null&&(y=v.value??""),n(T,y)}},validateAll:()=>Z(e,a,t),getValues:()=>a,getErrors:()=>s,validateField:T=>w(e,a,T,t)}}const pe=E.forwardRef(({dto:e,renderers:t,locale:n="en",handleChangeCallback:a},o)=>{const{values:s,handleChange:u,getValues:d,getErrors:x,validateAll:g,validateField:j}=K(e,n,a);return E.useImperativeHandle(o,()=>({getValues:d,getErrors:x,validateAll:g,validateField:j})),l.jsxs(l.Fragment,{children:[e.title&&l.jsx(c.Typography,{variant:"h5",color:"black",sx:{fontSize:e.titleFontSize?`${e.titleFontSize}rem`:"1.5rem",fontWeight:"bold"},gutterBottom:!0,children:f(e.title,n)}),e.description&&l.jsx(c.Typography,{component:"p",sx:{fontSize:e.descriptionFontSize?`${e.descriptionFontSize}rem`:"inherit"},color:"textSecondary",gutterBottom:!0,children:f(e.description,n)}),e.sections.map(R=>l.jsx(Q,{section:R,values:s,onChange:u,renderers:t,validateField:j,locale:n},R.id))]})});m.AutoCompleteField=U,m.CheckBoxInput=B,m.Field=X,m.FormBuilder=pe,m.MultiAutoCompleteField=G,m.RadioInput=H,m.Section=Q,m.SelectInput=M,m.TextAreaInput=J,m.TextInput=V,m.useFormBuilder=K,m.validateAll=Z,m.validateField=w,m.validationRules=de,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
|
|
6
|
+
<%s key={someKey} {...props} />`,b,h,_,h),se[h+b]=!0)}if(h=null,p!==void 0&&(n(p),h=""+p),u(i)&&(n(i.key),h=""+i.key),"key"in i){p={};for(var z in i)z!=="key"&&(p[z]=i[z])}else p=i;return h&&d(p,typeof r=="function"?r.displayName||r.name||"Unknown":r),g(r,h,p,o(),O,D)}function R(r){T(r)?r._store&&(r._store.validated=1):typeof r=="object"&&r!==null&&r.$$typeof===I&&(r._payload.status==="fulfilled"?T(r._payload.value)&&r._payload.value._store&&(r._payload.value._store.validated=1):r._store&&(r._store.validated=1))}function T(r){return typeof r=="object"&&r!==null&&r.$$typeof===y}var v=E,y=Symbol.for("react.transitional.element"),C=Symbol.for("react.portal"),$=Symbol.for("react.fragment"),me=Symbol.for("react.strict_mode"),fe=Symbol.for("react.profiler"),be=Symbol.for("react.consumer"),he=Symbol.for("react.context"),ve=Symbol.for("react.forward_ref"),xe=Symbol.for("react.suspense"),ge=Symbol.for("react.suspense_list"),ye=Symbol.for("react.memo"),I=Symbol.for("react.lazy"),Ee=Symbol.for("react.activity"),Te=Symbol.for("react.client.reference"),L=v.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ee=Object.prototype.hasOwnProperty,je=Array.isArray,N=console.createTask?console.createTask:function(){return null};v={react_stack_bottom_frame:function(r){return r()}};var re,te={},ne=v.react_stack_bottom_frame.bind(v,s)(),ae=N(a(s)),se={};S.Fragment=$,S.jsx=function(r,i,p){var b=1e4>L.recentlyCreatedOwnerStacks++;return j(r,i,p,!1,b?Error("react-stack-top-frame"):ne,b?N(a(r)):ae)},S.jsxs=function(r,i,p){var b=1e4>L.recentlyCreatedOwnerStacks++;return j(r,i,p,!0,b?Error("react-stack-top-frame"):ne,b?N(a(r)):ae)}})()),S}var W;function le(){return W||(W=1,process.env.NODE_ENV==="production"?k.exports=oe():k.exports=ue()),k.exports}var l=le();function f(e,t="en"){return e?typeof e=="string"?e:e[t]||e.en||Object.values(e)[0]||"":""}function F(e,t="en"){return e?e.map(n=>typeof n=="string"?{value:n,label:n}:{value:n.value,label:f(n.label,t)}):[]}function M({field:e,value:t,onChange:n,error:a,locale:o="en"}){const s=F(e.options||[],o);return l.jsxs(c.FormControl,{fullWidth:!0,children:[l.jsx(c.InputLabel,{id:`${e.id}-label`,children:f(e.label,o)}),l.jsx(c.Select,{labelId:`${e.id}-label`,value:t,id:e.id,name:e.id,onChange:u=>n(u.target.value),label:f(e.label,o),error:!!a,children:s?.map(u=>l.jsx(c.MenuItem,{value:u.value,children:u.label},u.label))}),a&&l.jsx(c.Typography,{variant:"caption",color:"error",children:a})]})}function V({field:e,value:t,onChange:n,error:a,locale:o}){return console.log("error",a),l.jsx(c.TextField,{fullWidth:!0,label:f(e.label,o),placeholder:f(e.placeholder||"",o),value:t||"",name:e.id,onChange:s=>n(s.target.value),disabled:e.disabled,type:e.type,slotProps:{inputLabel:{shrink:e.type==="date"?!0:void 0}},error:!!a,helperText:a})}function B({field:e,value:t,onChange:n,error:a,locale:o}){return l.jsxs(l.Fragment,{children:[l.jsx(c.FormControlLabel,{name:e.id,control:l.jsx(c.Checkbox,{checked:!!t,onChange:s=>n(s.target.checked),disabled:e.disabled}),label:f(e.label,o)}),a&&l.jsx(c.FormHelperText,{children:a})]})}const U=({field:e,value:t,onChange:n,error:a,locale:o})=>{const s=F(e.options||[],o);return l.jsx(c.Autocomplete,{fullWidth:!0,options:s||[],value:t||null,onChange:(u,d)=>n(d),renderInput:u=>l.jsx(c.TextField,{...u,name:e.id,label:f(e.label,o),placeholder:f(e.placeholder||"",o),disabled:e.disabled,error:!!a,helperText:a})})},G=({field:e,value:t,onChange:n,error:a,locale:o})=>{const s=F(e.options||[],o);return l.jsx(c.Autocomplete,{multiple:!0,fullWidth:!0,options:s||[],value:t||[],onChange:(u,d)=>n(d),renderInput:u=>l.jsx(c.TextField,{...u,name:e.id,label:f(e.label,o),placeholder:f(e.placeholder||"",o),disabled:e.disabled,error:!!a,helperText:a})})},J=({field:e,value:t,onChange:n,error:a,locale:o="en"})=>l.jsx(c.TextField,{fullWidth:!0,multiline:!0,rows:e.rows||4,label:f(e.label,o),placeholder:f(e.placeholder||"",o),value:t||"",onChange:s=>n(s.target.value),required:!!e.validations?.required,disabled:e.disabled,error:!!a,helperText:a}),H=({field:e,value:t,onChange:n,error:a,locale:o="en"})=>{const s=F(e.options||[],o);return l.jsxs(c.FormControl,{component:"fieldset",fullWidth:!0,error:!!a,children:[l.jsxs(c.FormLabel,{component:"legend",children:[f(e.label,o),e.validations?.required?" *":""]}),l.jsx(c.RadioGroup,{row:e.layout?.direction==="row"||!1,value:t||"",onChange:u=>n(u.target.value),children:(s||[]).map(u=>l.jsx(c.FormControlLabel,{value:u.value,control:l.jsx(c.Radio,{}),label:u.label,disabled:e.disabled},u.label))}),a&&l.jsx(c.FormHelperText,{children:a})]})},ie=({field:e,value:t,onChange:n,error:a,locale:o="en"})=>{switch(e.type){case"text":case"date":case"email":case"password":case"number":return l.jsx(V,{field:e,value:t,onChange:n,error:a,locale:o});case"select":return l.jsx(M,{field:e,value:t,onChange:n,error:a,locale:o});case"autocomplete":return l.jsx(U,{field:e,value:t,onChange:n,error:a,locale:o});case"multi-autocomplete":return l.jsx(G,{field:e,value:t,onChange:n,error:a,locale:o});case"checkbox":return l.jsx(B,{field:e,value:t,onChange:n,error:a,locale:o});case"textarea":return l.jsx(J,{field:e,value:t,onChange:n,error:a,locale:o});case"radio":return l.jsx(H,{field:e,value:t,onChange:n,error:a,locale:o});default:return l.jsxs("span",{children:["Unsupported field type: ",e.type]})}},X=({field:e,value:t,onChange:n,error:a,renderers:o={},locale:s="en"})=>{const u=o[e.type]||ie;return l.jsx(u,{field:e,value:t,onChange:n,error:a,locale:s})};function ce(e){const t=e??12;return{xs:12,sm:t,md:t,lg:t,xl:t}}const de={required:(e,t)=>e.required&&(t==null||t==="")?`${e.label} is required`:null,min:(e,t)=>e.type==="number"&&e.min!==void 0&&t<e.min?`${e.label} must be at least ${e.min}`:null,max:(e,t)=>e.type==="number"&&e.max!==void 0&&t>e.max?`${e.label} must be at most ${e.max}`:null,minLength:(e,t)=>typeof t=="string"&&e.minLength!==void 0&&t.length<e.minLength?`${e.label} must be at least ${e.minLength} characters`:null,maxLength:(e,t)=>typeof t=="string"&&e.maxLength!==void 0&&t.length>e.maxLength?`${e.label} must be at most ${e.maxLength} characters`:null,pattern:(e,t)=>e.pattern&&typeof t=="string"&&!e.pattern.test(t)?`${e.label} is invalid`:null,options:(e,t)=>e.type==="select"&&e.options&&!e.options.includes(t)?`${e.label} must be one of: ${e.options.join(", ")}`:null,dateRange:(e,t)=>{if(e.type==="date"&&t){const n=new Date(t);if(e.minDate&&n<new Date(e.minDate))return`${e.label} must be after ${e.minDate}`;if(e.maxDate&&n>new Date(e.maxDate))return`${e.label} must be before ${e.maxDate}`}return null},customValidator:(e,t)=>{if(e.customValidator&&typeof e.customValidator=="function"){const n=e.customValidator(t);if(typeof n=="string")return n}return null}},Z=(e,t,n="en")=>{const a={};return e.sections.forEach(o=>{o.fields.forEach(s=>{const u=w(e,t,s.id);u.length>0&&(a[s.id]=u)})}),a},w=(e,t,n,a="en")=>{const o=e.sections.flatMap(g=>g.fields).find(g=>g.id===n);if(!o)return[];const s=o.validations||{},u=t[n],d=[],x=f(o.label,a);if(s.required&&(u==null||u==="")&&d.push(typeof s.required=="string"?s.required:`${x} is required`),s.min!==void 0&&typeof u=="number"&&u<s.min&&d.push(`${x} must be at least ${s.min}`),s.max!==void 0&&typeof u=="number"&&u>s.max&&d.push(`${x} must be at most ${s.max}`),s.minLength!==void 0&&typeof u=="string"&&u.length<s.minLength&&d.push(`${x} must be at least ${s.minLength} characters`),s.maxLength!==void 0&&typeof u=="string"&&u.length>s.maxLength&&d.push(`${x} must be at most ${s.maxLength} characters`),s.pattern&&typeof u=="string"&&!s.pattern.test(u)&&d.push(`${x} is invalid`),s.validate){const g=s.validate(u);g&&d.push(f(g,a))}return d};function P(e,t){if(!e)return!0;if("field"in e){let n=t[e.field]??"";return Array.isArray(n)?n=n.map(a=>a.value??""):typeof n=="object"&&n!==null&&(n=n.value??""),e.equals!==void 0?n===e.equals:e.notEquals!==void 0?n!==e.notEquals:e.in!==void 0?Array.isArray(n)?e.in?n.some(a=>e.in.includes(a)):!1:e.in.includes(n):e.notIn!==void 0?Array.isArray(n)?n.every(a=>!e.notIn.includes(a)):!e.notIn.includes(n):e.greaterThan!==void 0?n>e.greaterThan:e.lessThan!==void 0?n<e.lessThan:!0}if("operator"in e){if(e.operator==="AND")return e.conditions.every(a=>P(a,t));if(e.operator==="OR")return e.conditions.some(n=>P(n,t))}return!1}const Q=({section:e,values:t,onChange:n,renderers:a,validateField:o,locale:s="en"})=>l.jsxs(c.Box,{mb:2,children:[e.heading&&l.jsx(c.Typography,{variant:"h6",sx:{fontSize:e.headingFontSize?`${e.headingFontSize}rem`:"1.25rem"},gutterBottom:!0,color:"black",children:f(e.heading,s)}),e.description&&l.jsx(c.Typography,{variant:"body2",sx:{fontSize:e.descriptionFontSize?`${e.descriptionFontSize}rem`:"inherit"},color:"textSecondary",gutterBottom:!0,children:f(e.description,s)}),l.jsx(c.Grid,{container:!0,spacing:2,children:e.fields.map(u=>P(u.visibleWhen,t)&&l.jsx(c.Grid,{size:ce(u.layout?.cols),children:l.jsx(X,{field:u,value:t[u.id],onChange:d=>n(u.id,d),renderers:a,error:o(u.id)?.join(","),locale:s})},u.id))})]});function K(e,t="en",n){const[a,o]=E.useState({}),[s,u]=E.useState({});return{values:a,handleChange:(T,v)=>{if(o(y=>({...y,[T]:v})),u(y=>({...y,[T]:null})),n){let y=v;Array.isArray(v)?y=v.map(C=>C.value??""):typeof v=="object"&&v!==null&&(y=v.value??""),n(T,y)}},validateAll:()=>Z(e,a,t),getValues:()=>a,getErrors:()=>s,validateField:T=>w(e,a,T,t)}}const pe=E.forwardRef(({dto:e,renderers:t,locale:n="en",handleChangeCallback:a},o)=>{const{values:s,handleChange:u,getValues:d,getErrors:x,validateAll:g,validateField:j}=K(e,n,a);return E.useImperativeHandle(o,()=>({getValues:d,getErrors:x,validateAll:g,validateField:j,handleChange:u})),l.jsxs(l.Fragment,{children:[e.title&&l.jsx(c.Typography,{variant:"h5",color:"black",sx:{fontSize:e.titleFontSize?`${e.titleFontSize}rem`:"1.5rem",fontWeight:"bold"},gutterBottom:!0,children:f(e.title,n)}),e.description&&l.jsx(c.Typography,{component:"p",sx:{fontSize:e.descriptionFontSize?`${e.descriptionFontSize}rem`:"inherit"},color:"textSecondary",gutterBottom:!0,children:f(e.description,n)}),e.sections.map(R=>l.jsx(Q,{section:R,values:s,onChange:u,renderers:t,validateField:j,locale:n},R.id))]})});m.AutoCompleteField=U,m.CheckBoxInput=B,m.Field=X,m.FormBuilder=pe,m.MultiAutoCompleteField=G,m.RadioInput=H,m.Section=Q,m.SelectInput=M,m.TextAreaInput=J,m.TextInput=V,m.useFormBuilder=K,m.validateAll=Z,m.validateField=w,m.validationRules=de,Object.defineProperty(m,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": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
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",
|