dynamic-mui 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/README.md +355 -37
  2. package/dist/accordion-DqBdGRim.mjs +23 -0
  3. package/dist/accordion-lvbD_BpX.js +1 -0
  4. package/dist/addressfield-CGX3UppE.mjs +152 -0
  5. package/dist/addressfield-DDG15fqJ.js +1 -0
  6. package/dist/asyncautocomplete-BAfBR9P7.mjs +89 -0
  7. package/dist/asyncautocomplete-w9q_1DWt.js +1 -0
  8. package/dist/bar-BzO7tvYJ.mjs +20 -0
  9. package/dist/bar-DEVxyUVS.js +1 -0
  10. package/dist/cascadeselect-C3QVfoig.mjs +64 -0
  11. package/dist/cascadeselect-DzbhK9en.js +1 -0
  12. package/dist/colorpicker-5qmEq7ZI.js +1 -0
  13. package/dist/colorpicker-DhNZulJY.mjs +98 -0
  14. package/dist/components/DynamicComponent.d.ts +2 -0
  15. package/dist/components/FormGenerator.d.ts +123 -2
  16. package/dist/components/controls/AsyncAutocomplete/asyncautocomplete.d.ts +9 -0
  17. package/dist/components/controls/CheckBox/checkbox.d.ts +1 -1
  18. package/dist/components/controls/ChipSelect/chipselect.d.ts +1 -1
  19. package/dist/components/controls/EditableTable/editabletable.d.ts +2 -0
  20. package/dist/components/controls/IntlPhone/intlphone.d.ts +2 -0
  21. package/dist/components/controls/LocationField/locationfield.d.ts +1 -1
  22. package/dist/components/controls/MultiTextbox/multitextbox.d.ts +1 -1
  23. package/dist/components/controls/NpsScale/npsscale.d.ts +3 -0
  24. package/dist/components/controls/NumberField/numberfield.d.ts +1 -1
  25. package/dist/components/controls/PasswordField/passwordfield.d.ts +1 -1
  26. package/dist/components/controls/Radio/radio.d.ts +1 -1
  27. package/dist/components/controls/RichTextEditor/richtexteditor.d.ts +3 -0
  28. package/dist/components/controls/Select/select.d.ts +1 -1
  29. package/dist/components/controls/Switch/switch.d.ts +1 -1
  30. package/dist/components/controls/TextField/textfield.d.ts +1 -1
  31. package/dist/components/controls/index.d.ts +45 -69
  32. package/dist/consentfield-B7Rm8d3U.js +1 -0
  33. package/dist/consentfield-CBQUR4J5.mjs +78 -0
  34. package/dist/datatable-BIQznwQe.mjs +20 -0
  35. package/dist/datatable-BWlcUP0M.js +1 -0
  36. package/dist/dynamic-mui.cjs +1 -0
  37. package/dist/dynamic-mui.mjs +32 -0
  38. package/dist/editabletable-DCoRmsEO.js +1 -0
  39. package/dist/editabletable-DRlBV8lk.mjs +126 -0
  40. package/dist/fileupload-ByBv1UjT.mjs +172 -0
  41. package/dist/fileupload-Cze3Oc_2.js +1 -0
  42. package/dist/formrepeater-BBkKGLFJ.mjs +71 -0
  43. package/dist/formrepeater-BxJzXoHx.js +1 -0
  44. package/dist/formwizard-BDZE1UDD.js +1 -0
  45. package/dist/formwizard-D2u1bMvK.mjs +83 -0
  46. package/dist/geofield-8WTeZk0Z.mjs +104 -0
  47. package/dist/geofield-Bw3p4NLJ.js +1 -0
  48. package/dist/group-B2mSylHd.mjs +23 -0
  49. package/dist/group-BlfO3ZZf.js +1 -0
  50. package/dist/imagelist-Bn9QhIv7.js +1 -0
  51. package/dist/imagelist-rqmJajHt.mjs +18 -0
  52. package/dist/index-CddjLTWF.mjs +12499 -0
  53. package/dist/index-D08GIfqF.js +43 -0
  54. package/dist/index.d.ts +11 -1
  55. package/dist/intlphone-B_Xn98pU.js +1 -0
  56. package/dist/intlphone-DtQZ12xk.mjs +140 -0
  57. package/dist/keyvaluefield-B_1NeGuP.mjs +125 -0
  58. package/dist/keyvaluefield-DyKCGd32.js +1 -0
  59. package/dist/line-B-DwR1hm.mjs +20 -0
  60. package/dist/line-BkymZu31.js +1 -0
  61. package/dist/lineitemlist-DGo8UOZC.js +1 -0
  62. package/dist/lineitemlist-vGKzJlQx.mjs +166 -0
  63. package/dist/list-7uNWrepS.mjs +20 -0
  64. package/dist/list-C0FgmtQw.js +1 -0
  65. package/dist/locationfield-CeUgxLkQ.mjs +119 -0
  66. package/dist/locationfield-Duyw1ZEz.js +1 -0
  67. package/dist/markdowneditor-B1y-hfny.mjs +90 -0
  68. package/dist/markdowneditor-Db0Un1_Z.js +2 -0
  69. package/dist/matrixfield-C7Sc-YpI.mjs +117 -0
  70. package/dist/matrixfield-CckODuPn.js +1 -0
  71. package/dist/mixChart-C3o184UT.js +1 -0
  72. package/dist/mixChart-ISf3Kbr-.mjs +75 -0
  73. package/dist/multitextbox-BYazIAeG.js +1 -0
  74. package/dist/multitextbox-ide2lvJ6.mjs +78 -0
  75. package/dist/npsscale-C2e88qk_.js +1 -0
  76. package/dist/npsscale-lndz7GEq.mjs +87 -0
  77. package/dist/numberstepper-DitbcvV9.mjs +128 -0
  78. package/dist/numberstepper-DyRjQM_K.js +1 -0
  79. package/dist/pie-CJSL8Sv5.js +1 -0
  80. package/dist/pie-CL29dlh5.mjs +20 -0
  81. package/dist/richtexteditor-CVg-MnKp.js +1 -0
  82. package/dist/richtexteditor-DsC1XC7h.mjs +141 -0
  83. package/dist/s3Upload-B0JeoLkS.js +1 -0
  84. package/dist/s3Upload-dpioZ2c8.mjs +51 -0
  85. package/dist/schema.d.ts +404 -0
  86. package/dist/signature-14OrZriO.mjs +1225 -0
  87. package/dist/signature-D582X6tB.js +2 -0
  88. package/dist/stepper-B-FpcZvT.js +1 -0
  89. package/dist/stepper-CDfEy_C6.mjs +140 -0
  90. package/dist/summaryfield-B08ZmVHh.js +1 -0
  91. package/dist/summaryfield-CTpPlZBG.mjs +96 -0
  92. package/dist/tabs-BIliqFOs.js +1 -0
  93. package/dist/tabs-DDeJCFt8.mjs +39 -0
  94. package/dist/tagsinput-D-q6rRZS.mjs +58 -0
  95. package/dist/tagsinput-qh_lhdFb.js +1 -0
  96. package/dist/types.d.ts +16 -0
  97. package/dist/util/registry.d.ts +29 -0
  98. package/dist/util/resolvers.d.ts +45 -0
  99. package/dist/util/useFormEngine.d.ts +74 -0
  100. package/dist/util/validateSchema.d.ts +23 -0
  101. package/dist/v4-BysszJq8.mjs +30 -0
  102. package/dist/v4-CqdK_b8Y.js +1 -0
  103. package/package.json +36 -11
  104. package/dist/XmlShapeDeserializer-3yQdm0b0.mjs +0 -237
  105. package/dist/XmlShapeDeserializer-XKJAoqLc.js +0 -1
  106. package/dist/date-utils-CFUUt6NL.js +0 -4
  107. package/dist/date-utils-pGzaKb0p.mjs +0 -477
  108. package/dist/dynamic-mui.cjs.js +0 -469
  109. package/dist/dynamic-mui.es.js +0 -102550
  110. package/dist/index-BW8_rRff.js +0 -7
  111. package/dist/index-BXFVK_GX.js +0 -1
  112. package/dist/index-CVqLvm8B.js +0 -1
  113. package/dist/index-Cr6Wp_S-.mjs +0 -3141
  114. package/dist/index-DA0XouZs.mjs +0 -153
  115. package/dist/index-DKS5Sz9e.js +0 -14
  116. package/dist/index-DcWfyXJp.mjs +0 -92
  117. package/dist/index-MllWgTJ5.mjs +0 -9372
  118. package/dist/index.browser-BSu11T_q.mjs +0 -363
  119. package/dist/index.browser-By7-GABP.js +0 -4
  120. package/dist/loadCognitoIdentity-DtYTy-DW.js +0 -1
  121. package/dist/loadCognitoIdentity-DzdR91GB.mjs +0 -872
  122. package/dist/loadSts-Cic6UToN.mjs +0 -593
  123. package/dist/loadSts-csbFP6zX.js +0 -1
  124. package/dist/main.d.ts +0 -1
  125. package/dist/noAuth-B8ui2kBg.mjs +0 -71
  126. package/dist/noAuth-JPA2o4dt.js +0 -1
  127. package/dist/playground/AIContext.d.ts +0 -18
  128. package/dist/playground/AIPanel.d.ts +0 -10
  129. package/dist/playground/Builder.d.ts +0 -1
  130. package/dist/playground/Canvas.d.ts +0 -9
  131. package/dist/playground/PropertiesEditor.d.ts +0 -10
  132. package/dist/playground/Sidebar.d.ts +0 -1
  133. package/dist/playground/documentation.d.ts +0 -6
  134. package/dist/playground/templates.d.ts +0 -7
  135. package/dist/playground/testData.d.ts +0 -2
  136. package/dist/test/setup.d.ts +0 -0
  137. package/dist/theme/theme.d.ts +0 -1
package/README.md CHANGED
@@ -1,67 +1,385 @@
1
1
  # dynamic-mui
2
2
 
3
- A powerful, dynamic form builder and generator for React using Material UI.
3
+ **Build rich, dynamic forms in React from a JSON schema — powered by Material UI.**
4
4
 
5
- ## Features (v2.0.0)
6
-
7
- - **Dynamic Form Generation**: Render complex forms from JSON schema.
8
- - **Visual Form Builder**: Drag-and-drop interface to build forms visually.
9
- - **New Documentation Mode**: Integrated documentation sidebar with component details.
10
- - **Advanced Validation**: Mandatory field validation with visual indicators (`*` and red error states).
11
- - **Rich Component Library**: Includes TextField, Select, Date/Time Pickers, Checkbox, Radio, Switch, Signature, and more.
12
- - **JSON Editing**: Edit form schema directly or use the visual builder.
13
- - **Data Preview**: View current form data in real-time.
14
-
15
- ## Installation
5
+ Render complex, reactive forms without writing form code. Fields can show/hide,
6
+ enable/disable, become required, compute their value, and drive each other's
7
+ options all declaratively from data. Comes with 45+ controls, a typed schema
8
+ helper, an imperative API, i18n, and an optional visual builder / AI module.
16
9
 
17
10
  ```bash
11
+ npm install dynamic-mui
12
+ # peer dependencies (if you don't already have them)
18
13
  npm install @mui/material @emotion/react @emotion/styled
19
- npm install dynamic-mui --save
20
14
  ```
21
15
 
22
- ## Usage
16
+ ---
17
+
18
+ ## Quick start
23
19
 
24
20
  ```tsx
25
- import { FormGenerator } from 'dynamic-mui';
21
+ import { FormGenerator, FormData } from 'dynamic-mui';
26
22
 
27
- const formSchema = [
23
+ const schema = [
28
24
  {
29
25
  type: 'textfield',
26
+ props: { id: 'name', MuiAttributes: { label: 'Full name', fullWidth: true } },
27
+ rules: { validation: [{ rule: 'mandatory', message: 'Name is required' }] },
28
+ layout: { row: 1, xs: 12, md: 6 },
29
+ },
30
+ {
31
+ type: 'select',
30
32
  props: {
31
- id: 'name',
32
- label: 'Full Name',
33
- MuiAttributes: { fullWidth: true, variant: 'outlined' },
34
- rules: {
35
- validation: [{ rule: 'mandatory', message: 'Name is required' }]
36
- }
33
+ id: 'role',
34
+ options: [{ value: 'dev', label: 'Developer' }, { value: 'design', label: 'Designer' }],
35
+ MuiBoxAttributes: { label: 'Role' },
37
36
  },
38
- layout: { row: 1, xs: 12, md: 6 }
39
- }
37
+ layout: { row: 1, xs: 12, md: 6 },
38
+ },
40
39
  ];
41
40
 
42
- export default function DynamicForm() {
43
- const handleSubmit = (data) => {
44
- console.log('Form Data:', data);
45
- };
46
-
41
+ export default function MyForm() {
47
42
  return (
48
43
  <FormGenerator
49
- data={formSchema}
50
- onSubmit={handleSubmit}
44
+ guid="my-form"
45
+ data={schema}
46
+ onSubmit={(values, errors) => {
47
+ if (errors.length === 0) console.log(FormData('my-form'));
48
+ }}
51
49
  />
52
50
  );
53
51
  }
54
52
  ```
55
53
 
56
- ## Local Development (Playground)
54
+ Every field is an object of `{ type, props, layout, rules?, ...engine }`.
55
+ `layout` uses MUI Grid units (`row`, `xs`, `sm`, `md`, `lg`).
56
+
57
+ ---
58
+
59
+ ## Reading & writing form data
60
+
61
+ Data lives in a store keyed by the form's `guid`:
62
+
63
+ ```ts
64
+ import { FormData, ClearFormData, useForm } from 'dynamic-mui';
65
+
66
+ FormData('my-form'); // { name: 'Ada', role: 'dev' }
67
+ ClearFormData('my-form'); // reset one form (or all forms if called with no guid)
68
+
69
+ const values = useForm('my-form'); // reactive hook inside a component
70
+ ```
71
+
72
+ Prefill a form with `patch`:
73
+
74
+ ```tsx
75
+ <FormGenerator guid="my-form" data={schema} patch={{ name: 'Ada', role: 'dev' }} />
76
+ ```
77
+
78
+ Conditional fields, subforms, computed values, and `requiredWhen` all evaluate
79
+ correctly from the **initial** `patch` — not just after the first edit.
80
+
81
+ ### Imperative API (`apiRef`)
82
+
83
+ ```tsx
84
+ import { useRef } from 'react';
85
+ import { FormGenerator, FormApi } from 'dynamic-mui';
86
+
87
+ const api = useRef<FormApi>(null);
88
+
89
+ <FormGenerator guid="my-form" data={schema} apiRef={api} />;
90
+
91
+ api.current?.getValues(); // current values
92
+ api.current?.setValues({ ... }); // patch values in
93
+ api.current?.validate(); // boolean — runs validation, shows errors
94
+ api.current?.getErrors(); // array of { id, rule, message }
95
+ api.current?.reset(); // clear values
96
+ api.current?.submit(); // trigger the submit flow
97
+
98
+ // Dirty tracking
99
+ api.current?.isDirty(); // changed since it loaded?
100
+ api.current?.getInitialValues(); // the starting snapshot
101
+ api.current?.resetToInitial(); // undo all edits
102
+ api.current?.markPristine(); // adopt current values as the new baseline (after save)
103
+
104
+ // Print & PDF (see below)
105
+ api.current?.print({ title: 'Report' });
106
+ await api.current?.exportPdf({ title: 'Report', filename: 'report.pdf' });
107
+ ```
108
+
109
+ Set `warnOnUnsavedChanges` to prompt the user before they leave the page while
110
+ the form is dirty.
111
+
112
+ ---
113
+
114
+ ## Review, read-only, print & PDF
115
+
116
+ ```tsx
117
+ // Read-only: full layout, every control disabled
118
+ <FormGenerator guid="f" data={schema} patch={values} readOnly />
119
+
120
+ // Review: a clean label → value summary instead of inputs
121
+ <FormGenerator guid="f" data={schema} patch={values} reviewMode />
122
+ ```
123
+
124
+ The review summary resolves option labels (not raw values), Yes/No for booleans,
125
+ joins multi-values, skips hidden and display-only fields, and follows matching
126
+ subforms — ideal for a "confirm before submit" step.
127
+
128
+ `print()` opens the browser dialog for that same summary (users can Save as PDF)
129
+ — **no dependencies**. `exportPdf()` downloads a real PDF and needs the optional
130
+ `pdfmake` package (lazy-loaded, never bundled into the core):
131
+
132
+ ```bash
133
+ npm i pdfmake # only if you use exportPdf()
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Submit bar
139
+
140
+ Render a visible (optionally sticky) submit button, fully configurable:
141
+
142
+ ```tsx
143
+ <FormGenerator
144
+ guid="wo" data={schema}
145
+ stickySubmit // pins to the bottom of the scroll area
146
+ cancelLabel="Cancel" onCancel={...}
147
+ submitButton={{
148
+ label: 'Dispatch work order',
149
+ color: 'success', // MUI palette name or any CSS color
150
+ gradient: ['#f59e0b', '#ef4444'],// optional gradient fill (overrides color)
151
+ icon: 'engineering', // leading Material icon; also `endIcon`
152
+ variant: 'contained', // contained | outlined | text
153
+ loadingLabel: 'Dispatching…',
154
+ }}
155
+ onSubmit={async (values, errors) => { if (!errors.length) await save(values); }}
156
+ />
157
+ ```
158
+
159
+ If `onSubmit` returns a Promise, the button shows a **spinner automatically**
160
+ until it settles (or drive it yourself with `submitButton.loading`).
161
+ `submitLabel="Save"` is a shortcut for `submitButton={{ label: 'Save' }}`.
162
+
163
+ ---
164
+
165
+ ## Dynamic rule engine
166
+
167
+ Add these keys to any field to make the form reactive. All conditions run
168
+ against the live form values.
169
+
170
+ | Key | What it does |
171
+ | ------------- | --------------------------------------------------------- |
172
+ | `visibleWhen` | Show the field only when a condition holds |
173
+ | `disabledWhen`| Disable the field when a condition holds |
174
+ | `requiredWhen`| Make the field required when a condition holds |
175
+ | `formula` | Compute the field's value from other fields |
176
+ | `dependsOn` + `optionsMap` | Swap a field's options based on another field |
177
+ | `subforms` | Reveal a group of fields for a matching parent value |
178
+
179
+ A condition is `{ field, op, value }` with operators `eq`, `neq`, `gt`, `gte`,
180
+ `lt`, `lte`, `in`, `nin`, `contains`, `startsWith`, `endsWith`, `empty`,
181
+ `notEmpty`, `truthy`, `falsy`, plus `all` / `any` / `none` groups.
182
+
183
+ ```ts
184
+ // Show + require "reason" only when status is "other"
185
+ {
186
+ type: 'textfield',
187
+ props: { id: 'reason', MuiAttributes: { label: 'Reason' } },
188
+ visibleWhen: { field: 'status', op: 'eq', value: 'other' },
189
+ requiredWhen: { field: 'status', op: 'eq', value: 'other' },
190
+ requiredMessage: 'Please tell us why',
191
+ layout: { row: 2, xs: 12 },
192
+ }
193
+
194
+ // Live total = qty * price
195
+ {
196
+ type: 'computed',
197
+ props: { id: 'total', format: '0,0.00', MuiAttributes: { label: 'Total' } },
198
+ formula: 'qty * price',
199
+ layout: { row: 3, xs: 12 },
200
+ }
201
+
202
+ // State options depend on the selected country (one control, not many)
203
+ {
204
+ type: 'select',
205
+ props: { id: 'state', MuiBoxAttributes: { label: 'State' } },
206
+ dependsOn: 'country',
207
+ optionsMap: {
208
+ us: [{ value: 'ca', label: 'California' }],
209
+ in: [{ value: 'ka', label: 'Karnataka' }],
210
+ },
211
+ layout: { row: 4, xs: 12 },
212
+ }
213
+ ```
214
+
215
+ `formula` supports `+ - * / %`, parentheses, unary minus, and
216
+ `SUM/AVG/MIN/MAX/ROUND/ABS/FLOOR/CEIL`.
217
+
218
+ ---
57
219
 
58
- To run the local playground and test changes:
220
+ ## Validation
221
+
222
+ Per-field rules live under `rules.validation`:
223
+
224
+ ```ts
225
+ rules: { validation: [{ rule: 'mandatory', message: 'Required' }] }
226
+ ```
227
+
228
+ Cross-field validators compare against another field:
229
+ `equalsField`, `notEqualsField`, `gtField`, `gteField`, `ltField`, `lteField`.
230
+
231
+ ```ts
232
+ // "confirm" must equal "password"
233
+ rules: { validation: [{ rule: 'equalsField', field: 'password', message: 'Passwords must match' }] }
234
+ ```
235
+
236
+ Turn on an accessible summary and auto-focus of the first error with
237
+ `validationSummary`. Hidden (`visibleWhen`) fields are excluded from validation.
238
+
239
+ ---
240
+
241
+ ## Typed schema (`defineForm`)
242
+
243
+ Get full TypeScript autocompletion and compile-time checks for your schema:
244
+
245
+ ```ts
246
+ import { defineForm, FormGenerator } from 'dynamic-mui';
247
+
248
+ const schema = defineForm([
249
+ { type: 'textfield', props: { id: 'email', MuiAttributes: { label: 'Email' } }, layout: { row: 1, xs: 12 } },
250
+ ]);
251
+
252
+ <FormGenerator guid="typed" data={schema} />;
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Extensibility & DX
258
+
259
+ - **Custom controls** — register your own field types; they get the same
260
+ `ControlProps` as built-ins and participate fully in the engine:
261
+ ```ts
262
+ import { registerControl } from 'dynamic-mui';
263
+ registerControl('color-swatch', ({ attributes, onChange }) => /* your component */);
264
+ // then: { type: 'color-swatch', props: { id: 'brand' } }
265
+ ```
266
+ - **Zod / Yup validation** — validate the whole form against an existing schema
267
+ (`zod`/`yup` are never bundled — the resolvers duck-type the object you pass):
268
+ ```ts
269
+ import { zodResolver } from 'dynamic-mui';
270
+ <FormGenerator data={fields} resolver={zodResolver(z.object({ email: z.string().email() }))} />
271
+ ```
272
+ - **Async / remote validation** — per-field server checks (debounced, race-safe,
273
+ gates submit): `asyncValidators={{ username: async (v) => taken(v) ? 'Taken' : null }}`.
274
+ - **Typed values** — `FormApi<T>`, `FormData<T>(guid)`, `useForm<T>(guid)` give
275
+ autocompleted, type-checked values.
276
+
277
+ ### Headless engine — `useFormEngine`
278
+
279
+ The whole dynamic engine (rules, validation, dynamic options, subforms) with **no
280
+ UI** — render your own components:
281
+
282
+ ```tsx
283
+ import { useFormEngine, zodResolver } from 'dynamic-mui';
284
+
285
+ function MyForm() {
286
+ const form = useFormEngine(schema, { resolver: zodResolver(mySchema) });
287
+ return (
288
+ <form onSubmit={(e) => { e.preventDefault(); form.submit(save); }}>
289
+ {form.visibleFields.map((f) => (
290
+ <MyInput key={f.id} value={f.value} required={f.required}
291
+ disabled={f.disabled} options={f.options} error={f.error}
292
+ onChange={(v) => form.setValue(f.id, v)} />
293
+ ))}
294
+ </form>
295
+ );
296
+ }
297
+ ```
298
+
299
+ Exposes `values`, `errors`, `fields`/`visibleFields`, `setValue`/`setValues`,
300
+ `validate()`, `submit(onValid)`, `isValid`, `isDirty`, `reset()`, `getFieldState(id)`.
301
+
302
+ ### Schema linter — `validateSchema`
303
+
304
+ Catch config mistakes before runtime (great in a test or build step):
305
+
306
+ ```ts
307
+ import { validateSchema } from 'dynamic-mui';
308
+
309
+ const issues = validateSchema(mySchema); // SchemaIssue[]; [] = clean
310
+ // flags duplicate ids, dependsOn → missing field, orphan optionsMap,
311
+ // rule/cross-field references to unknown fields, subform conditions
312
+ // that aren't real options, …
313
+ if (issues.some((i) => i.level === 'error')) throw new Error('Bad form schema');
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Internationalisation
319
+
320
+ Override the built-in strings:
321
+
322
+ ```tsx
323
+ <FormGenerator guid="fr" data={schema}
324
+ messages={{ required: 'Champ obligatoire', errorSummary: '{n} champ(s) à corriger' }} />
325
+ ```
326
+
327
+ Or plug in **any** i18n system with a `translate` function — it localizes every
328
+ user-facing string (labels, placeholders, helper text, option labels, validation
329
+ messages, typography, submit/cancel labels), keeping the schema in one language:
330
+
331
+ ```tsx
332
+ // works with i18next, react-intl, or a plain dictionary
333
+ const t = (s) => dictionary[s] ?? s;
334
+
335
+ <FormGenerator guid="form" data={schema} translate={t} />
336
+ ```
337
+
338
+ Switching the language re-renders the form translated while preserving values.
339
+
340
+ ---
341
+
342
+ ## Controls
343
+
344
+ Text & numbers · password · OTP · phone / international phone · currency ·
345
+ select · async autocomplete · autocomplete · cascade select · chip select ·
346
+ radio · checkbox · switch · toggle buttons · slider · rating · NPS · stepper ·
347
+ date / time / date-time / date-range pickers · file upload · signature ·
348
+ color picker · markdown & rich-text editors · tags · key-value · matrix ·
349
+ editable & data tables · line items · address · geo / location · consent ·
350
+ alert · divider · typography · charts (bar / line / pie / mixed), and more.
351
+
352
+ Each control is configured through `props` (its `MuiAttributes` /
353
+ `MuiBoxAttributes` map straight onto the underlying MUI component).
354
+
355
+ ### Theming
356
+
357
+ Controls render under **your** MUI theme. Date/time pickers automatically match
358
+ the size and variant of your text fields, so a picker lines up with the fields
359
+ beside it.
360
+
361
+ ---
362
+
363
+ ## AI module (optional)
364
+
365
+ `dynamic-mui/ai` provides AI-assisted building blocks (generate a form from a
366
+ prompt, autofill, text assist, schema review) that talk to **your** proxy/relay
367
+ so your provider key stays server-side. See [`examples/`](./examples) for a
368
+ ready-to-deploy proxy (including an AWS Lambda BYOK relay).
369
+
370
+ ---
371
+
372
+ ## Playground
373
+
374
+ ```bash
375
+ npm install
376
+ npm run dev # visual builder + live demo gallery
377
+ ```
59
378
 
60
- 1. `npm install`
61
- 2. `npm run dev`
62
- 3. Open `http://localhost:5173` (or the port shown in terminal).
379
+ Build forms visually, browse every control in multiple configurations, explore
380
+ real-world example forms, view the generated JSON, and copy it into your app.
63
381
 
64
- The playground allows you to build forms visually, view the generated JSON, and test validation.
382
+ ---
65
383
 
66
384
  ## License
67
385
 
@@ -0,0 +1,23 @@
1
+ import { jsxs as a, jsx as o } from "react/jsx-runtime";
2
+ import m from "react";
3
+ import { Accordion as s, AccordionSummary as l, Typography as p, Icon as u, AccordionDetails as f } from "@mui/material";
4
+ import { F as A } from "./index-CddjLTWF.mjs";
5
+ import { v as h } from "./v4-BysszJq8.mjs";
6
+ function j({ attributes: r = {}, patch: c, onChange: i }) {
7
+ const { MuiAttributes: n = {}, label: e = "Accordion", subFields: t = [] } = r, d = m.useMemo(() => h(), []);
8
+ return /* @__PURE__ */ a(s, { ...n, children: [
9
+ /* @__PURE__ */ o(l, { expandIcon: /* @__PURE__ */ o(u, { children: "expand_more" }), children: /* @__PURE__ */ o(p, { children: e }) }),
10
+ /* @__PURE__ */ o(f, { children: /* @__PURE__ */ o(
11
+ A,
12
+ {
13
+ guid: d,
14
+ data: t,
15
+ patch: c,
16
+ onChange: i
17
+ }
18
+ ) })
19
+ ] });
20
+ }
21
+ export {
22
+ j as default
23
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),u=require("react"),r=require("@mui/material"),a=require("./index-D08GIfqF.js"),l=require("./v4-CqdK_b8Y.js");function x({attributes:n={},patch:o,onChange:c}){const{MuiAttributes:i={},label:t="Accordion",subFields:s=[]}=n,d=u.useMemo(()=>l.v4(),[]);return e.jsxs(r.Accordion,{...i,children:[e.jsx(r.AccordionSummary,{expandIcon:e.jsx(r.Icon,{children:"expand_more"}),children:e.jsx(r.Typography,{children:t})}),e.jsx(r.AccordionDetails,{children:e.jsx(a.FormGenerator,{guid:d,data:s,patch:o,onChange:c})})]})}exports.default=x;
@@ -0,0 +1,152 @@
1
+ import { jsxs as f, jsx as e } from "react/jsx-runtime";
2
+ import S, { useCallback as A } from "react";
3
+ import r from "@mui/material/Grid";
4
+ import i from "@mui/material/TextField";
5
+ import g from "@mui/material/MenuItem";
6
+ import { u as E, m as P, a as I } from "./index-CddjLTWF.mjs";
7
+ const N = [
8
+ { value: "US", label: "United States" },
9
+ { value: "CA", label: "Canada" },
10
+ { value: "MX", label: "Mexico" },
11
+ { value: "GB", label: "United Kingdom" },
12
+ { value: "IE", label: "Ireland" },
13
+ { value: "FR", label: "France" },
14
+ { value: "DE", label: "Germany" },
15
+ { value: "ES", label: "Spain" },
16
+ { value: "IT", label: "Italy" },
17
+ { value: "NL", label: "Netherlands" },
18
+ { value: "BE", label: "Belgium" },
19
+ { value: "CH", label: "Switzerland" },
20
+ { value: "SE", label: "Sweden" },
21
+ { value: "NO", label: "Norway" },
22
+ { value: "DK", label: "Denmark" },
23
+ { value: "PT", label: "Portugal" },
24
+ { value: "AU", label: "Australia" },
25
+ { value: "NZ", label: "New Zealand" },
26
+ { value: "IN", label: "India" },
27
+ { value: "CN", label: "China" },
28
+ { value: "JP", label: "Japan" },
29
+ { value: "SG", label: "Singapore" },
30
+ { value: "AE", label: "United Arab Emirates" },
31
+ { value: "BR", label: "Brazil" },
32
+ { value: "ZA", label: "South Africa" }
33
+ ], U = {
34
+ street1: "Street address",
35
+ street2: "Apt, suite, etc. (optional)",
36
+ city: "City",
37
+ state: "State / Province",
38
+ postalCode: "Postal code",
39
+ country: "Country"
40
+ };
41
+ function D({ attributes: n = {}, rules: h = {}, onChange: c }) {
42
+ const {
43
+ id: b = "",
44
+ MuiAttributes: v = {},
45
+ countries: o,
46
+ labels: y = {}
47
+ } = n, x = Array.isArray(o) && o.length ? o : N, l = { ...U, ...y }, d = h?.validation?.some(
48
+ (t) => t.rule === "mandatory" || t.rule === "mandatoryselect"
49
+ ) || !1, [a, p] = S.useState(() => ({
50
+ street1: "",
51
+ street2: "",
52
+ city: "",
53
+ state: "",
54
+ postalCode: "",
55
+ country: "",
56
+ ...n?.value || {}
57
+ }));
58
+ E(() => {
59
+ p({
60
+ street1: "",
61
+ street2: "",
62
+ city: "",
63
+ state: "",
64
+ postalCode: "",
65
+ country: "",
66
+ ...n?.value || {}
67
+ });
68
+ }, [n?.value]);
69
+ const s = A(
70
+ (t) => (C) => {
71
+ const m = { ...a, [t]: C.target.value };
72
+ p(m), c?.({ id: b, value: m });
73
+ },
74
+ [a, b, c]
75
+ ), u = P(I, v?.sx);
76
+ return /* @__PURE__ */ f(r, { container: !0, spacing: 2, ...v, children: [
77
+ /* @__PURE__ */ e(r, { size: { xs: 12 }, children: /* @__PURE__ */ e(
78
+ i,
79
+ {
80
+ fullWidth: !0,
81
+ label: l.street1,
82
+ required: d,
83
+ value: a.street1 || "",
84
+ onChange: s("street1"),
85
+ inputProps: { "aria-label": l.street1 },
86
+ sx: u
87
+ }
88
+ ) }),
89
+ /* @__PURE__ */ e(r, { size: { xs: 12 }, children: /* @__PURE__ */ e(
90
+ i,
91
+ {
92
+ fullWidth: !0,
93
+ label: l.street2,
94
+ value: a.street2 || "",
95
+ onChange: s("street2"),
96
+ inputProps: { "aria-label": l.street2 },
97
+ sx: u
98
+ }
99
+ ) }),
100
+ /* @__PURE__ */ e(r, { size: { xs: 12, sm: 6, md: 5 }, children: /* @__PURE__ */ e(
101
+ i,
102
+ {
103
+ fullWidth: !0,
104
+ label: l.city,
105
+ required: d,
106
+ value: a.city || "",
107
+ onChange: s("city"),
108
+ inputProps: { "aria-label": l.city },
109
+ sx: u
110
+ }
111
+ ) }),
112
+ /* @__PURE__ */ e(r, { size: { xs: 12, sm: 6, md: 4 }, children: /* @__PURE__ */ e(
113
+ i,
114
+ {
115
+ fullWidth: !0,
116
+ label: l.state,
117
+ value: a.state || "",
118
+ onChange: s("state"),
119
+ inputProps: { "aria-label": l.state },
120
+ sx: u
121
+ }
122
+ ) }),
123
+ /* @__PURE__ */ e(r, { size: { xs: 12, sm: 6, md: 3 }, children: /* @__PURE__ */ e(
124
+ i,
125
+ {
126
+ fullWidth: !0,
127
+ label: l.postalCode,
128
+ value: a.postalCode || "",
129
+ onChange: s("postalCode"),
130
+ inputProps: { "aria-label": l.postalCode },
131
+ sx: u
132
+ }
133
+ ) }),
134
+ /* @__PURE__ */ e(r, { size: { xs: 12, sm: 6 }, children: /* @__PURE__ */ e(
135
+ i,
136
+ {
137
+ select: !0,
138
+ fullWidth: !0,
139
+ label: l.country,
140
+ required: d,
141
+ value: a.country || "",
142
+ onChange: s("country"),
143
+ SelectProps: { inputProps: { "aria-label": l.country } },
144
+ sx: u,
145
+ children: x.map((t) => /* @__PURE__ */ e(g, { value: t.value, children: t.label }, t.value))
146
+ }
147
+ ) })
148
+ ] });
149
+ }
150
+ export {
151
+ D as default
152
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),h=require("react"),s=require("@mui/material/Grid"),n=require("@mui/material/TextField"),A=require("@mui/material/MenuItem"),c=require("./index-D08GIfqF.js"),g=[{value:"US",label:"United States"},{value:"CA",label:"Canada"},{value:"MX",label:"Mexico"},{value:"GB",label:"United Kingdom"},{value:"IE",label:"Ireland"},{value:"FR",label:"France"},{value:"DE",label:"Germany"},{value:"ES",label:"Spain"},{value:"IT",label:"Italy"},{value:"NL",label:"Netherlands"},{value:"BE",label:"Belgium"},{value:"CH",label:"Switzerland"},{value:"SE",label:"Sweden"},{value:"NO",label:"Norway"},{value:"DK",label:"Denmark"},{value:"PT",label:"Portugal"},{value:"AU",label:"Australia"},{value:"NZ",label:"New Zealand"},{value:"IN",label:"India"},{value:"CN",label:"China"},{value:"JP",label:"Japan"},{value:"SG",label:"Singapore"},{value:"AE",label:"United Arab Emirates"},{value:"BR",label:"Brazil"},{value:"ZA",label:"South Africa"}],f={street1:"Street address",street2:"Apt, suite, etc. (optional)",city:"City",state:"State / Province",postalCode:"Postal code",country:"Country"};function P({attributes:i={},rules:m={},onChange:b}){const{id:v="",MuiAttributes:x={},countries:o,labels:C={}}=i,S=Array.isArray(o)&&o.length?o:g,l={...f,...C},d=m?.validation?.some(t=>t.rule==="mandatory"||t.rule==="mandatoryselect")||!1,[a,p]=h.useState(()=>({street1:"",street2:"",city:"",state:"",postalCode:"",country:"",...i?.value||{}}));c.useUpdateEffect(()=>{p({street1:"",street2:"",city:"",state:"",postalCode:"",country:"",...i?.value||{}})},[i?.value]);const r=h.useCallback(t=>j=>{const y={...a,[t]:j.target.value};p(y),b?.({id:v,value:y})},[a,v,b]),u=c.mergeSx(c.premiumInputSx,x?.sx);return e.jsxs(s,{container:!0,spacing:2,...x,children:[e.jsx(s,{size:{xs:12},children:e.jsx(n,{fullWidth:!0,label:l.street1,required:d,value:a.street1||"",onChange:r("street1"),inputProps:{"aria-label":l.street1},sx:u})}),e.jsx(s,{size:{xs:12},children:e.jsx(n,{fullWidth:!0,label:l.street2,value:a.street2||"",onChange:r("street2"),inputProps:{"aria-label":l.street2},sx:u})}),e.jsx(s,{size:{xs:12,sm:6,md:5},children:e.jsx(n,{fullWidth:!0,label:l.city,required:d,value:a.city||"",onChange:r("city"),inputProps:{"aria-label":l.city},sx:u})}),e.jsx(s,{size:{xs:12,sm:6,md:4},children:e.jsx(n,{fullWidth:!0,label:l.state,value:a.state||"",onChange:r("state"),inputProps:{"aria-label":l.state},sx:u})}),e.jsx(s,{size:{xs:12,sm:6,md:3},children:e.jsx(n,{fullWidth:!0,label:l.postalCode,value:a.postalCode||"",onChange:r("postalCode"),inputProps:{"aria-label":l.postalCode},sx:u})}),e.jsx(s,{size:{xs:12,sm:6},children:e.jsx(n,{select:!0,fullWidth:!0,label:l.country,required:d,value:a.country||"",onChange:r("country"),SelectProps:{inputProps:{"aria-label":l.country}},sx:u,children:S.map(t=>e.jsx(A,{value:t.value,children:t.label},t.value))})})]})}exports.default=P;