@anri1214/dynamic-forms-mui 0.1.7
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/LICENSE +22 -0
- package/README.md +403 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.js +1831 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 AI Hub
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# @ai-hub/dynamic-forms-mui
|
|
2
|
+
|
|
3
|
+
Type-safe declarative form builder for React with Material-UI.
|
|
4
|
+
|
|
5
|
+
Define your forms with a JSON schema — get fully validated, accessible MUI forms with zero boilerplate.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **JSON-driven forms** — describe fields, sections, and validation in a single schema object
|
|
10
|
+
- **15 built-in field types** — text, email, password, textarea, number, select, multiselect, autocomplete, checkbox, switch, radio, date, date-range, file upload
|
|
11
|
+
- **Dynamic validation** — Zod schemas generated at runtime from your config; async validation supported
|
|
12
|
+
- **Conditional visibility** — show/hide fields based on other field values
|
|
13
|
+
- **Dynamic options** — load select/radio/autocomplete options from an API with dependency support
|
|
14
|
+
- **Async search** — autocomplete fields with server-side search via `dependsOnInput`
|
|
15
|
+
- **Customizable section styles** — override spacing and styles per section slot via `sectionSlotProps`
|
|
16
|
+
- **Fully customizable UI** — replace any or all default MUI components via Context
|
|
17
|
+
- **API client injection** — bring your own HTTP client (Axios, fetch, etc.)
|
|
18
|
+
- **Type-safe** — full TypeScript coverage for schemas, fields, and components
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @ai-hub/dynamic-forms-mui
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Peer Dependencies
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled react-hook-form zod @tanstack/react-query @base-ui/react
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Wrap your app with `DynamicFormsProvider`
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { DynamicFormsProvider } from '@ai-hub/dynamic-forms-mui'
|
|
38
|
+
import type { ApiClient } from '@ai-hub/dynamic-forms-mui'
|
|
39
|
+
|
|
40
|
+
// Adapt your HTTP client to the ApiClient interface
|
|
41
|
+
const apiClient: ApiClient = {
|
|
42
|
+
get: (url, config) => fetch(url).then((r) => r.json()),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function App() {
|
|
46
|
+
return (
|
|
47
|
+
<DynamicFormsProvider
|
|
48
|
+
apiClient={apiClient}
|
|
49
|
+
onError={(msg) => console.error(msg)}
|
|
50
|
+
>
|
|
51
|
+
{/* your app */}
|
|
52
|
+
</DynamicFormsProvider>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Define a form schema
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import type { FormSchema } from '@ai-hub/dynamic-forms-mui'
|
|
61
|
+
|
|
62
|
+
const schema: FormSchema = {
|
|
63
|
+
sections: [
|
|
64
|
+
{
|
|
65
|
+
id: 'contact',
|
|
66
|
+
title: 'Contact Info',
|
|
67
|
+
fields: ['name', 'email'],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
fields: {
|
|
71
|
+
name: {
|
|
72
|
+
type: 'text',
|
|
73
|
+
label: 'Full Name',
|
|
74
|
+
placeholder: 'John Doe',
|
|
75
|
+
validation: { required: true, minLength: 2 },
|
|
76
|
+
},
|
|
77
|
+
email: {
|
|
78
|
+
type: 'email',
|
|
79
|
+
label: 'Email',
|
|
80
|
+
validation: { required: true },
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 3. Render the form
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { DynamicForm } from '@ai-hub/dynamic-forms-mui'
|
|
90
|
+
import type { LabelMap } from '@ai-hub/dynamic-forms-mui'
|
|
91
|
+
|
|
92
|
+
function ContactPage() {
|
|
93
|
+
const handleSubmit = (data: Record<string, unknown>, labelMap: LabelMap) => {
|
|
94
|
+
console.log('Form data:', data)
|
|
95
|
+
console.log('Labels:', labelMap) // { country: { 'US': 'United States', ... }, ... }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<DynamicForm schema={schema} onSubmit={handleSubmit}>
|
|
100
|
+
{({ isValid, isSubmitting }) => (
|
|
101
|
+
<button type="submit" disabled={!isValid || isSubmitting}>
|
|
102
|
+
Submit
|
|
103
|
+
</button>
|
|
104
|
+
)}
|
|
105
|
+
</DynamicForm>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Field Types
|
|
111
|
+
|
|
112
|
+
| Type | Config Interface | Description |
|
|
113
|
+
|------|-----------------|-------------|
|
|
114
|
+
| `text` | `TextFieldConfig` | Standard text input |
|
|
115
|
+
| `email` | `TextFieldConfig` | Email input with format validation |
|
|
116
|
+
| `password` | `TextFieldConfig` | Password input |
|
|
117
|
+
| `textarea` | `TextFieldConfig` | Multi-line text input |
|
|
118
|
+
| `number` | `NumberFieldConfig` | Numeric input with min/max/step |
|
|
119
|
+
| `select` | `SelectFieldConfig` | Single-value dropdown |
|
|
120
|
+
| `multiselect` | `SelectFieldConfig` | Multi-value dropdown with chips |
|
|
121
|
+
| `autocomplete` | `AutocompleteFieldConfig` | Single-value autocomplete with async search |
|
|
122
|
+
| `checkbox` | `CheckboxFieldConfig` | Boolean checkbox |
|
|
123
|
+
| `switch` | `CheckboxFieldConfig` | Toggle switch |
|
|
124
|
+
| `radio` | `RadioFieldConfig` | Radio button group |
|
|
125
|
+
| `date` | `DateFieldConfig` | Date picker |
|
|
126
|
+
| `dateRange` | `DateFieldConfig` | Date range picker |
|
|
127
|
+
| `file` | `FileFieldConfig` | File upload |
|
|
128
|
+
|
|
129
|
+
## Validation
|
|
130
|
+
|
|
131
|
+
Validation rules are defined per-field in the `validation` property:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
validation: {
|
|
135
|
+
required: true,
|
|
136
|
+
minLength: 2,
|
|
137
|
+
maxLength: 100,
|
|
138
|
+
pattern: '^[a-zA-Z]+$',
|
|
139
|
+
patternMessage: 'Only letters allowed',
|
|
140
|
+
|
|
141
|
+
// Async validation (e.g. check username availability)
|
|
142
|
+
asyncValidation: {
|
|
143
|
+
apiUrl: '/api/check-username',
|
|
144
|
+
debounceMs: 500,
|
|
145
|
+
message: 'Username is already taken',
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Conditional Fields
|
|
151
|
+
|
|
152
|
+
Show or hide fields based on other values:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
fields: {
|
|
156
|
+
hasCompany: {
|
|
157
|
+
type: 'checkbox',
|
|
158
|
+
label: 'I have a company',
|
|
159
|
+
},
|
|
160
|
+
companyName: {
|
|
161
|
+
type: 'text',
|
|
162
|
+
label: 'Company Name',
|
|
163
|
+
showIf: { field: 'hasCompany', operator: 'equals', value: true },
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Supported operators: `equals`, `notEquals`, `in`, `notIn`, `isEmpty`, `isNotEmpty`, `gt`, `gte`, `lt`, `lte`. Conditions can be combined with `and` / `or`.
|
|
169
|
+
|
|
170
|
+
## Dynamic Options
|
|
171
|
+
|
|
172
|
+
Load select/radio/autocomplete options from an API:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
country: {
|
|
176
|
+
type: 'select',
|
|
177
|
+
label: 'Country',
|
|
178
|
+
optionsSource: {
|
|
179
|
+
apiUrl: '/api/countries',
|
|
180
|
+
valueKey: 'code',
|
|
181
|
+
labelKey: 'name',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
city: {
|
|
185
|
+
type: 'select',
|
|
186
|
+
label: 'City',
|
|
187
|
+
optionsSource: {
|
|
188
|
+
apiUrl: '/api/cities',
|
|
189
|
+
queryParams: [{ key: 'country', dependsOn: 'country' }],
|
|
190
|
+
valueKey: 'id',
|
|
191
|
+
labelKey: 'name',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
When `country` changes, `city` options reload automatically.
|
|
197
|
+
|
|
198
|
+
### Async Search (Autocomplete)
|
|
199
|
+
|
|
200
|
+
Use `dependsOnInput` in `queryParams` to send the user's typed text as a search query:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
customer: {
|
|
204
|
+
type: 'autocomplete',
|
|
205
|
+
label: 'Customer',
|
|
206
|
+
optionsSource: {
|
|
207
|
+
apiUrl: '/api/customers',
|
|
208
|
+
queryParams: [{ key: 'search', dependsOnInput: true }],
|
|
209
|
+
valueKey: 'id',
|
|
210
|
+
labelKey: 'name',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The input is debounced (300ms) before triggering the API call.
|
|
216
|
+
|
|
217
|
+
### Pre-selected Options (`initialOptions`)
|
|
218
|
+
|
|
219
|
+
When editing a form with pre-existing values, the API options may not have loaded yet. Use `initialOptions` to display the current selection immediately:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
companies: {
|
|
223
|
+
type: 'multiselect',
|
|
224
|
+
label: 'Companies',
|
|
225
|
+
optionsSource: { apiUrl: '/api/companies', valueKey: 'id', labelKey: 'name' },
|
|
226
|
+
initialOptions: [
|
|
227
|
+
{ value: '1', label: 'Acme Corp' },
|
|
228
|
+
{ value: '2', label: 'Globex' },
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`initialOptions` are merged with API results — duplicates are deduplicated automatically.
|
|
234
|
+
|
|
235
|
+
## Customizing UI Components
|
|
236
|
+
|
|
237
|
+
Replace any default MUI component with your own:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { DynamicFormsProvider } from '@ai-hub/dynamic-forms-mui'
|
|
241
|
+
import type { DFTextFieldProps } from '@ai-hub/dynamic-forms-mui'
|
|
242
|
+
|
|
243
|
+
function MyTextField(props: DFTextFieldProps) {
|
|
244
|
+
return <input className="my-input" {...props} />
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
<DynamicFormsProvider
|
|
248
|
+
apiClient={apiClient}
|
|
249
|
+
components={{ TextField: MyTextField }}
|
|
250
|
+
>
|
|
251
|
+
{/* Only TextField is replaced; the other 9 stay default */}
|
|
252
|
+
</DynamicFormsProvider>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Overridable Components
|
|
256
|
+
|
|
257
|
+
`TextField` · `NumberField` · `Select` · `Autocomplete` · `Checkbox` · `Switch` · `RadioGroup` · `DatePicker` · `DateRangePicker` · `FileUpload`
|
|
258
|
+
|
|
259
|
+
## Section Styling (`sectionSlotProps`)
|
|
260
|
+
|
|
261
|
+
Customize spacing and styles for section elements via `sectionSlotProps`. Can be set globally via the provider or per-form (form-level merges on top of provider-level):
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
// Global defaults for all forms
|
|
265
|
+
<DynamicFormsProvider
|
|
266
|
+
apiClient={apiClient}
|
|
267
|
+
sectionSlotProps={{
|
|
268
|
+
root: { sx: { mb: 2 } },
|
|
269
|
+
content: { sx: { gap: 1 } },
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
{/* Per-form override — merges with provider defaults */}
|
|
273
|
+
<DynamicForm
|
|
274
|
+
schema={schema}
|
|
275
|
+
onSubmit={handleSubmit}
|
|
276
|
+
sectionSlotProps={{
|
|
277
|
+
title: { sx: { mb: 1, fontSize: '1rem' } },
|
|
278
|
+
description: { sx: { mb: 1 } },
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
{({ isValid }) => <button type="submit" disabled={!isValid}>Save</button>}
|
|
282
|
+
</DynamicForm>
|
|
283
|
+
</DynamicFormsProvider>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Available slots: `root` (section wrapper), `title` (section heading), `description` (section description), `content` (fields container). Custom `sx` is merged on top of defaults. When both provider and form define the same slot, `sx` values are merged with form-level taking priority.
|
|
287
|
+
|
|
288
|
+
Section `title` is optional — omit it from the schema to render sections without a heading.
|
|
289
|
+
|
|
290
|
+
## Component Size
|
|
291
|
+
|
|
292
|
+
By default, all form inputs use MUI's `"medium"` size. You can set `size` globally via the provider or per-form:
|
|
293
|
+
|
|
294
|
+
```tsx
|
|
295
|
+
// All forms render with small inputs
|
|
296
|
+
<DynamicFormsProvider apiClient={apiClient} size="small">
|
|
297
|
+
{/* ... */}
|
|
298
|
+
</DynamicFormsProvider>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Per-form override (takes priority over the provider):
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
<DynamicForm schema={schema} onSubmit={handleSubmit} size="small" />
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Supported values: `"small"` | `"medium"` (default).
|
|
308
|
+
|
|
309
|
+
## Per-Form API Client Override
|
|
310
|
+
|
|
311
|
+
Override the global `apiClient` for a specific form:
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
<DynamicForm
|
|
315
|
+
schema={schema}
|
|
316
|
+
onSubmit={handleSubmit}
|
|
317
|
+
apiClient={differentApiClient}
|
|
318
|
+
/>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Custom Field Types
|
|
322
|
+
|
|
323
|
+
Register custom field components for new field types:
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { registerFieldComponent } from '@ai-hub/dynamic-forms-mui'
|
|
327
|
+
|
|
328
|
+
registerFieldComponent('rating', MyRatingField)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Label Map (`onSubmit` second argument)
|
|
332
|
+
|
|
333
|
+
The `onSubmit` callback receives a second argument — `labelMap` of type `LabelMap` — a nested record mapping field names to their loaded option labels:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
type LabelMap = Record<string, Record<string | number, string>>
|
|
337
|
+
// { [fieldName]: { [optionValue]: optionLabel } }
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
This is useful for multi-step forms where you need to display human-readable labels in a review step without making extra API calls:
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
const [formData, setFormData] = useState<Record<string, unknown>>({})
|
|
344
|
+
const [labels, setLabels] = useState<LabelMap>({})
|
|
345
|
+
|
|
346
|
+
const handleSubmit = (data: Record<string, unknown>, labelMap: LabelMap) => {
|
|
347
|
+
setFormData(data) // { country: 'US', city: '42' }
|
|
348
|
+
setLabels(labelMap) // { country: { 'US': 'United States', 'UK': 'United Kingdom', ... }, city: { '42': 'New York', ... } }
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// In the review step:
|
|
352
|
+
const countryLabel = labels.country?.[formData.country as string] // "United States"
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The label map includes all currently loaded options for fields with `options`, `optionsSource`, or `initialOptions`. Fields without options (text, number, checkbox, etc.) are not included.
|
|
356
|
+
|
|
357
|
+
## Exports
|
|
358
|
+
|
|
359
|
+
### Components
|
|
360
|
+
- `DynamicFormsProvider` — context provider (wrap your app)
|
|
361
|
+
- `DynamicForm` — main form renderer
|
|
362
|
+
- `FormErrorBoundary` — error boundary for forms
|
|
363
|
+
|
|
364
|
+
### Hooks
|
|
365
|
+
- `useDynamicFormsContext()` — access apiClient, components, notifications
|
|
366
|
+
- `useFieldVisibility(schema)` — get visible field set
|
|
367
|
+
- `useOptionsSource(source, staticOptions, initialOptions?)` — load options from API with async search support
|
|
368
|
+
- `useAsyncValidation(fieldName, config)` — debounced async validation
|
|
369
|
+
|
|
370
|
+
### Utils
|
|
371
|
+
- `buildZodSchema` — generate Zod schema from FormSchema
|
|
372
|
+
- `evaluateCondition` — evaluate showIf conditions
|
|
373
|
+
- `extractDefaultValues` — extract defaults from schema
|
|
374
|
+
- `registerFieldComponent` — register custom field types
|
|
375
|
+
|
|
376
|
+
### Types
|
|
377
|
+
- `FormSchema`, `Section`, `FieldConfig`, `FieldType`, `FormMode`, `SectionSlotProps`
|
|
378
|
+
- `AutocompleteFieldConfig`, `SelectFieldConfig`, `TextFieldConfig`, etc.
|
|
379
|
+
- `ApiClient`, `UIComponents`, `NotificationCallbacks`, `ComponentSize`
|
|
380
|
+
- `LabelMap` — nested record of resolved option labels (`Record<fieldName, Record<value, label>>`)
|
|
381
|
+
- `DFTextFieldProps`, `DFAutocompleteProps`, `DFSelectProps`, `DFCheckboxProps`, etc.
|
|
382
|
+
|
|
383
|
+
## AI Assistant Integration (MCP)
|
|
384
|
+
|
|
385
|
+
This library has a companion MCP server — **@ai-hub/dynamic-forms-mui-mcp** — that lets AI assistants (Cursor, Claude Code, VS Code Copilot) understand the `FormSchema` structure and help generate correct form configurations.
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npx @ai-hub/dynamic-forms-mui-mcp
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The MCP server provides:
|
|
392
|
+
- **Schema validation** — validate JSON against the FormSchema spec
|
|
393
|
+
- **Field type docs** — descriptions, props, and examples for all 14 field types
|
|
394
|
+
- **Code generation** — generate FormSchema JSON from natural language
|
|
395
|
+
- **Backend endpoint generation** — produce API endpoints in any language/framework
|
|
396
|
+
- **Version-aware** — serves documentation matching your installed library version
|
|
397
|
+
|
|
398
|
+
See [@ai-hub/dynamic-forms-mui-mcp README](../dynamic-forms-mui-mcp/README.md) for setup instructions.
|
|
399
|
+
|
|
400
|
+
## License
|
|
401
|
+
|
|
402
|
+
MIT
|
|
403
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),y=require("react"),m=require("@mui/material"),L=require("@base-ui/react/number-field"),je=require("@mui/icons-material/KeyboardArrowUp"),Ce=require("@mui/icons-material/KeyboardArrowDown"),Se=require("@mui/x-date-pickers/DatePicker"),De=require("@mui/x-date-pickers/DateTimePicker"),G=require("@mui/icons-material"),x=require("react-hook-form"),F=require("zod"),qe=require("@tanstack/react-query");function we(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}const Te=we(y),H=y.createContext(null);function R(){const e=y.useContext(H);if(!e)throw new Error("useDynamicFormsContext must be used within a DynamicFormsProvider. Wrap your app (or the part that uses DynamicForm) with <DynamicFormsProvider>.");return e}function Re({variant:e="outlined",...t}){return u.jsx(m.TextField,{variant:e,fullWidth:!0,...t})}function te(e){return null}te.muiName="Input";function ke({label:e,error:t,helperText:r,placeholder:n,size:a="medium",ref:i,...s}){const l=Te.useId(),c=a==="small"?"small":"medium";return u.jsxs(L.NumberField.Root,{...s,render:(o,d)=>u.jsx(m.FormControl,{size:a,ref:o.ref,disabled:d.disabled,required:d.required,error:t,variant:"outlined",fullWidth:!0,children:o.children}),children:[u.jsx(te,{...s}),u.jsx(m.InputLabel,{htmlFor:l,children:e}),u.jsx(L.NumberField.Input,{id:l,render:(o,d)=>u.jsx(m.OutlinedInput,{label:e,placeholder:n,size:a,inputRef:i||o.ref,value:d.inputValue,onBlur:o.onBlur,onChange:o.onChange,onKeyUp:o.onKeyUp,onKeyDown:o.onKeyDown,onFocus:o.onFocus,slotProps:{input:o},endAdornment:u.jsxs(m.InputAdornment,{position:"end",sx:{flexDirection:"column",maxHeight:"unset",alignSelf:"stretch",borderLeft:"1px solid",borderColor:"divider",ml:0,"& button":{py:0,flex:1,borderRadius:.5}},children:[u.jsx(L.NumberField.Increment,{render:u.jsx(m.IconButton,{size:c,"aria-label":"Increase"}),children:u.jsx(je,{fontSize:c,sx:{transform:"translateY(2px)"}})}),u.jsx(L.NumberField.Decrement,{render:u.jsx(m.IconButton,{size:c,"aria-label":"Decrease"}),children:u.jsx(Ce,{fontSize:c,sx:{transform:"translateY(-2px)"}})})]}),sx:{pr:0}})}),r&&u.jsx(m.FormHelperText,{sx:{ml:0},children:r})]})}function ze({options:e,onChange:t,ref:r,size:n,...a}){return u.jsx(m.Select,{...a,size:n,onChange:t,children:e.map(i=>u.jsx(m.MenuItem,{value:i.value,disabled:i.disabled,children:i.label},i.value))})}function Ee({label:e,placeholder:t,error:r,helperText:n,required:a,size:i,...s}){return u.jsx(m.Autocomplete,{...s,size:i,getOptionLabel:l=>typeof l=="string"?l:l.label,renderInput:l=>u.jsx(m.TextField,{...l,label:e,placeholder:t,error:r,helperText:n,required:a})})}function Ie({label:e,error:t,helperText:r,size:n,ref:a,...i}){return u.jsxs(m.FormControl,{error:t,size:n,children:[u.jsx(m.FormControlLabel,{control:u.jsx(m.Checkbox,{...i,size:n}),label:e}),r&&u.jsx(m.FormHelperText,{children:r})]})}function Ae({label:e,error:t,helperText:r,size:n,ref:a,...i}){return u.jsxs(m.FormControl,{error:t,size:n,children:[u.jsx(m.FormControlLabel,{control:u.jsx(m.Switch,{...i,size:n}),label:e}),r&&u.jsx(m.FormHelperText,{children:r})]})}function Pe({label:e,options:t,error:r,helperText:n,disabled:a,required:i,size:s,...l}){return u.jsxs(m.FormControl,{component:"fieldset",error:r,disabled:a,required:i,size:s,children:[e&&u.jsx(m.FormLabel,{component:"legend",children:e}),u.jsx(m.RadioGroup,{...l,children:t.map(c=>u.jsx(m.FormControlLabel,{value:c.value,control:u.jsx(m.Radio,{size:s}),label:c.label},c.value))}),n&&u.jsx(m.FormHelperText,{children:n})]})}function W({includeTime:e,error:t,helperText:r,required:n,format:a,size:i,ref:s,...l}){const c=e?De.DateTimePicker:Se.DatePicker;return u.jsx(c,{...l,format:a,inputRef:s,slotProps:{textField:{error:t,helperText:r,required:n,fullWidth:!0,size:i}}})}function Ve({value:e,onChange:t,label:r,disabled:n,error:a,helperText:i,minDate:s,maxDate:l,required:c,includeTime:o,format:d,size:f}){const h=j=>{t?.({start:j,end:e?.end??null})},p=j=>{t?.({start:e?.start??null,end:j})},v=o?"Start Date/Time":"Start Date",b=o?"End Date/Time":"End Date";return u.jsxs(m.Box,{children:[r&&u.jsxs(m.Typography,{variant:"body2",sx:{mb:1,color:a?"error.main":"text.secondary"},children:[r,c&&u.jsx("span",{style:{color:"inherit"},children:" *"})]}),u.jsxs(m.Box,{sx:{display:"flex",gap:2},children:[u.jsx(W,{label:v,includeTime:o,format:d,value:e?.start??null,onChange:h,disabled:n,minDate:s,maxDate:e?.end??l,error:a,size:f}),u.jsx(W,{label:b,includeTime:o,format:d,value:e?.end??null,onChange:p,disabled:n,minDate:e?.start??s,maxDate:l,error:a,size:f})]}),i&&u.jsx(m.Typography,{variant:"caption",sx:{mt:.5,ml:1.75,color:a?"error.main":"text.secondary"},children:i})]})}function re(e){if(e===0)return"0 KB";const t=1024,r=t*1024,n=r*1024;return e>=n?`${(e/n).toFixed(2)} GB`:e>=r?`${(e/r).toFixed(2)} MB`:`${(e/t).toFixed(2)} KB`}function Oe({value:e,onChange:t,onBlur:r,multiple:n,accept:a,label:i,disabled:s,error:l,helperText:c,required:o,ref:d}){const[f,h]=y.useState(!1),p=y.useRef(null),v=y.useId(),b=y.useId(),j=g=>{g.preventDefault(),g.stopPropagation(),s||h(!0)},S=g=>{g.preventDefault(),g.stopPropagation(),h(!1)},w=g=>{g.preventDefault(),g.stopPropagation()},D=(g,T)=>{const I=new Set(g.map(z=>`${z.name}-${z.size}`));return T.filter(z=>!I.has(`${z.name}-${z.size}`))},C=g=>{if(g.preventDefault(),g.stopPropagation(),h(!1),s)return;const T=Array.from(g.dataTransfer.files);if(T.length!==0)if(n){const I=Array.isArray(e)?e:[],z=D(I,T);t?.([...I,...z])}else t?.(T[0]||null)},q=g=>{const T=g.target.files;if(!T||T.length===0)return;const I=Array.from(T);if(n){const z=Array.isArray(e)?e:[],Fe=D(z,I);t?.([...z,...Fe])}else t?.(I[0]||null);p.current&&(p.current.value="")},k=()=>{t?.(null),p.current&&(p.current.value="")},P=g=>{if(!Array.isArray(e))return;const T=e.filter((I,z)=>z!==g);t?.(T.length>0?T:null),T.length===0&&p.current&&(p.current.value="")},E=g=>{s||(g.key==="Enter"||g.key===" ")&&(g.preventDefault(),p.current?.click())},$=e?Array.isArray(e)?e:[e]:[],be=()=>l?"error.main":f?"primary.main":"divider",ve=()=>s?"divider":l?"error.main":"primary.main";return u.jsxs(m.FormControl,{fullWidth:!0,error:l,children:[i&&u.jsxs(m.Typography,{component:"label",htmlFor:v,variant:"body2",sx:{mb:1,display:"block",fontWeight:500},children:[i,o&&u.jsx(m.Typography,{component:"span",sx:{ml:.5},children:"*"})]}),u.jsxs(m.Box,{role:"button",tabIndex:s?-1:0,"aria-label":`Upload ${n?"files":"file"}${i?` for ${i}`:""}`,"aria-disabled":s,"aria-invalid":l,"aria-required":o,"aria-describedby":c?b:void 0,onDragEnter:j,onDragOver:w,onDragLeave:S,onDrop:C,onKeyDown:E,sx:{border:2,borderStyle:"dashed",borderColor:be(),borderRadius:1,p:3,textAlign:"center",bgcolor:f?"action.hover":"background.paper",cursor:s?"not-allowed":"pointer",transition:"all 0.2s ease-in-out","&:hover":{borderColor:ve(),bgcolor:s?"background.paper":"action.hover"}},onClick:()=>!s&&p.current?.click(),children:[u.jsx(G.CloudUpload,{sx:{fontSize:48,color:l?"error.main":"primary.main",mb:1}}),u.jsxs(m.Typography,{variant:"body1",color:s?"text.disabled":"text.primary",children:["Drag & drop ",n?"files":"a file"," or click to select"]}),a&&u.jsxs(m.Typography,{variant:"caption",color:"text.secondary",sx:{mt:.5,display:"block"},children:["Accepted types: ",a]})]}),u.jsx("input",{id:v,ref:g=>{p.current=g,typeof d=="function"?d(g):d&&(d.current=g)},type:"file",accept:a,multiple:n,onChange:q,onBlur:r,style:{display:"none"},disabled:s,required:o,"aria-invalid":l,"aria-required":o,"aria-describedby":c?b:void 0}),$.length>0&&u.jsx(m.Box,{sx:{mt:2},role:"status","aria-live":"polite","aria-label":`${$.length} ${$.length===1?"file":"files"} selected`,children:$.map((g,T)=>u.jsxs(m.Box,{sx:{display:"flex",alignItems:"center",justifyContent:"space-between",p:1.5,mb:1,border:1,borderColor:"divider",borderRadius:1,bgcolor:"background.paper","&:last-child":{mb:0}},children:[u.jsxs(m.Box,{sx:{flex:1,minWidth:0},children:[u.jsx(m.Typography,{variant:"body2",sx:{fontWeight:500,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:g.name}),u.jsx(m.Typography,{variant:"caption",color:"text.secondary",children:re(g.size)})]}),u.jsx(m.IconButton,{size:"small",onClick:I=>{I.stopPropagation(),n?P(T):k()},disabled:s,"aria-label":`Remove ${g.name}`,sx:{ml:1},children:u.jsx(G.Close,{fontSize:"small"})})]},`${g.name}-${g.size}-${T}`))}),c&&u.jsx(m.FormHelperText,{id:b,children:c})]})}function $e(){return{TextField:Re,NumberField:ke,Select:ze,Autocomplete:Ee,Checkbox:Ie,Switch:Ae,RadioGroup:Pe,DatePicker:W,DateRangePicker:Ve,FileUpload:Oe}}function Le({apiClient:e,components:t,size:r,sectionSlotProps:n,onError:a,children:i}){const l={...$e(),...t},o={apiClient:e,components:l,notifications:{onError:a??console.error},size:r,sectionSlotProps:n};return u.jsx(H,{value:o,children:i})}function A(e,t){return"and"in e?e.and.every(r=>A(r,t)):"or"in e?e.or.some(r=>A(r,t)):Ne(e,t)}function Ne(e,t){const r=t[e.field],n=e.value;switch(e.operator){case"equals":return r===n;case"notEquals":return r!==n;case"in":return Array.isArray(n)&&n.includes(r);case"notIn":return Array.isArray(n)&&!n.includes(r);case"isEmpty":return M(r);case"isNotEmpty":return!M(r);case"gt":return typeof r=="number"&&typeof n=="number"&&r>n;case"gte":return typeof r=="number"&&typeof n=="number"&&r>=n;case"lt":return typeof r=="number"&&typeof n=="number"&&r<n;case"lte":return typeof r=="number"&&typeof n=="number"&&r<=n;default:return!1}}function M(e){return e==null?!0:typeof e=="string"?e.trim()==="":Array.isArray(e)?e.length===0:!1}function Q(e){if(e==null||e==="")return;if(e instanceof Date)return isNaN(e.getTime())||e.getTime()===0?void 0:e;const t=new Date(e);if(!(isNaN(t.getTime())||t.getTime()===0))return t}function ne(e){const t={};for(const[n,a]of Object.entries(e.fields))t[n]=Be(a);let r=F.z.object(t);return r=Ue(r,e),r}function Be(e){const{type:t,validation:r={}}=e;let n;switch(t){case"number":n=Me(r);break;case"checkbox":case"switch":n=F.z.boolean();break;case"date":{let a=F.z.preprocess(Q,F.z.date({message:r.messages?.invalidDate||"Invalid date"}).optional());r.required&&(a=a.refine(i=>i!=null&&i instanceof Date&&!isNaN(i.getTime())&&i.getTime()!==0,{message:r.messages?.required||"Date is required"})),r.minDate&&(a=a.refine(i=>!i||i>=r.minDate,{message:r.messages?.minDate||`Date must be after ${r.minDate.toLocaleDateString()}`})),r.maxDate&&(a=a.refine(i=>!i||i<=r.maxDate,{message:r.messages?.maxDate||`Date must be before ${r.maxDate.toLocaleDateString()}`})),n=a;break}case"dateRange":{const a=F.z.preprocess(Q,F.z.date({message:r.messages?.invalidDate||"Invalid date"}).optional().nullable());let i=F.z.object({start:a,end:a});r.required&&(i=i.refine(s=>{const l=s.start!==null&&s.start!==void 0&&s.start instanceof Date&&!isNaN(s.start.getTime())&&s.start.getTime()!==0,c=s.end!==null&&s.end!==void 0&&s.end instanceof Date&&!isNaN(s.end.getTime())&&s.end.getTime()!==0;return l&&c},{message:r.messages?.required||"Both start and end dates are required",path:["start"]})),i=i.refine(s=>!s.start||!s.end?!0:s.start<=s.end,{message:r.messages?.invalidRange||"End date must be after start date",path:["end"]}),n=i;break}case"autocomplete":case"multiselect":n=t==="autocomplete"?F.z.string():F.z.array(F.z.string()),r.required&&(t==="autocomplete"?n=n.min(1,r.messages?.required||"Required"):n=n.min(1,r.messages?.required||"Select at least one option"));break;case"file":{const a=e;let i=a.multiple?F.z.array(F.z.instanceof(File)).nullable():F.z.instanceof(File).nullable();r.required&&(a.multiple?i=i.refine(s=>s===null?!1:s.length>=1,{message:r.messages?.required||"At least one file is required"}):i=i.refine(s=>s!=null,{message:r.messages?.required||"File is required"})),r.maxSize&&(a.multiple?i=i.refine(s=>s===null?!0:s.every(l=>l?.size<=r.maxSize),{message:r.messages?.maxSize||`File size must be less than ${r.maxSize/1024/1024}MB`}):i=i.refine(s=>s===null?!0:s.size<=r.maxSize,{message:r.messages?.maxSize||`File size must be less than ${r.maxSize/1024/1024}MB`})),a.accept&&(a.multiple?i=i.refine(s=>s===null?!0:s.every(l=>a.accept.split(",").map(o=>o.trim()).some(o=>o.startsWith(".")?l.name.toLowerCase().endsWith(o.toLowerCase()):l.type.match(o.replace("*",".*")))),{message:r.messages?.fileType||`Invalid file type. Accepted: ${a.accept}`}):i=i.refine(s=>{if(s===null)return!0;const l=s;return a.accept.split(",").map(o=>o.trim()).some(o=>o.startsWith(".")?l.name?.toLowerCase().endsWith(o.toLowerCase()):l.type?.match(o.replace("*",".*")))},{message:r.messages?.fileType||`Invalid file type. Accepted: ${a.accept}`})),r.minFiles&&a.multiple&&(i=i.refine(s=>s===null?!1:s.length>=r.minFiles,{message:r.messages?.minFiles||`At least ${r.minFiles} file(s) required`})),r.maxFiles&&a.multiple&&(i=i.refine(s=>s===null?!0:s.length<=r.maxFiles,{message:r.messages?.maxFiles||`Max ${r.maxFiles} file(s) allowed`})),n=i;break}default:n=_e(r)}return!r.required&&!r.requiredIf&&(n=n.optional()),n}function _e(e){if(e.email)return e.required?F.z.string().min(1,{message:e.messages?.required||"Email is required"}).pipe(F.z.email({message:e.messages?.email||"Invalid email"})):F.z.union([F.z.literal(""),F.z.email({message:e.messages?.email||"Invalid email"})]);if(e.url)return e.required?F.z.string().min(1,{message:e.messages?.required||"URL is required"}).pipe(F.z.url({message:e.messages?.url||"Invalid URL"})):F.z.union([F.z.literal(""),F.z.url({message:e.messages?.url||"Invalid URL"})]);let t=F.z.string();if(e.required&&(t=t.min(1,e.messages?.required||"Required")),e.minLength&&(t=t.min(e.minLength,e.messages?.minLength||`Min ${e.minLength} characters`)),e.maxLength&&(t=t.max(e.maxLength,e.messages?.maxLength||`Max ${e.maxLength} characters`)),e.pattern&&(t=t.regex(new RegExp(e.pattern),e.messages?.pattern||"Invalid format")),e.hasUppercase){const r=e.hasUppercase===!0?1:e.hasUppercase,n=new RegExp(`(?=(.*[A-Z]){${r}})`);t=t.regex(n,e.messages?.hasUppercase||`At least ${r} uppercase letter(s)`)}if(e.hasLowercase){const r=e.hasLowercase===!0?1:e.hasLowercase,n=new RegExp(`(?=(.*[a-z]){${r}})`);t=t.regex(n,e.messages?.hasLowercase||`At least ${r} lowercase letter(s)`)}if(e.hasNumber){const r=e.hasNumber===!0?1:e.hasNumber,n=new RegExp(`(?=(.*[0-9]){${r}})`);t=t.regex(n,e.messages?.hasNumber||`At least ${r} number(s)`)}if(e.hasSpecialChar){const r=e.hasSpecialChar===!0?1:e.hasSpecialChar,n=new RegExp(`(?=(.*[!@#$%^&*()_+\\-=\\[\\]{};':"\\\\|,.<>\\/?]){${r}})`);t=t.regex(n,e.messages?.hasSpecialChar||`At least ${r} special character(s)`)}return e.includes&&(t=t.includes(e.includes,{message:e.messages?.includes||`Must include "${e.includes}"`})),t}function Me(e){let t=F.z.coerce.number();return e.min!==void 0&&(t=t.min(e.min,e.messages?.min||`Min value is ${e.min}`)),e.max!==void 0&&(t=t.max(e.max,e.messages?.max||`Max value is ${e.max}`)),t}function Ue(e,t){const r=[],n=[];for(const[i,s]of Object.entries(t.fields))s.validation?.matchField&&r.push({fieldName:i,matchField:s.validation.matchField,message:s.validation.messages?.matchField||"Fields do not match"}),s.validation?.requiredIf&&n.push({fieldName:i,condition:s.validation.requiredIf,message:s.validation.messages?.required||`${s.label} is required`});let a=e;for(const{fieldName:i,matchField:s,message:l}of r)a=a.refine(c=>c[i]===c[s],{message:l,path:[i]});return n.length>0&&(a=a.superRefine((i,s)=>{for(const{fieldName:l,condition:c,message:o}of n)if(A(c,i)){const d=i[l];M(d)&&s.issues.push({code:"custom",message:o,path:[l],input:d})}})),a}function se(e,t,r){const n=new URL(e.apiUrl,window.location.origin);if(e.queryParams)for(const a of e.queryParams){let i;if(a.dependsOnInput)i=r||void 0;else if(a.dependsOn){const s=t[a.dependsOn];i=s!=null?String(s):void 0}else a.value&&(i=a.value);i!==void 0&&n.searchParams.set(a.key,i)}return n.pathname+n.search}function ie(e){return e.queryParams?e.queryParams.filter(t=>t.dependsOn).map(t=>t.dependsOn):[]}function oe(e,t){return e.every(r=>{const n=t[r];return n!=null&&n!==""})}function ae(e){const t={};for(const[r,n]of Object.entries(e.fields))if(n.defaultValue!==void 0)t[r]=n.defaultValue;else switch(n.type){case"text":case"email":case"password":case"textarea":case"select":case"autocomplete":case"radio":t[r]="";break;case"number":t[r]=null;break;case"checkbox":case"switch":t[r]=!1;break;case"multiselect":t[r]=[];break;case"date":t[r]=void 0;break;case"dateRange":t[r]={start:void 0,end:void 0};break;case"file":t[r]=n.multiple?[]:null;break;default:t[r]=""}return t}const V=(e,t)=>{if(!t)return e;const r=Array.isArray(e)?e:[e],n=Array.isArray(t)?t:[t];return[...r,...n]},N=(e,t)=>e?t?{sx:V(e.sx??{},t.sx)}:e:t,We=(e,t)=>e?t?{root:N(e.root,t.root),title:N(e.title,t.title),description:N(e.description,t.description),content:N(e.content,t.content)}:e:t;function le(e){const{control:t}=x.useFormContext(),r=[];for(const[c,o]of Object.entries(e.fields))if(o.showIf){const d=K(o.showIf);r.push({name:c,dependsOn:d})}const n=[],a=new Set;for(const{dependsOn:c}of r)for(const o of c)a.has(o)||(a.add(o),n.push(o));const i=x.useWatch({control:t,name:n}),s={};n.length>0&&i&&n.forEach((c,o)=>{s[c]=Array.isArray(i)?i[o]:i});const l=new Set;for(const[c,o]of Object.entries(e.fields))o.showIf?A(o.showIf,s)&&l.add(c):l.add(c);return l}function K(e){return"and"in e?e.and.flatMap(K):"or"in e?e.or.flatMap(K):[e.field]}function O(e,t,r){const{control:n}=x.useFormContext(),{apiClient:a,notifications:i}=R(),s=e?.queryParams?.some(q=>q.dependsOnInput)??!1,[l,c]=y.useState(""),[o,d]=y.useState("");y.useEffect(()=>{if(!s)return;const q=setTimeout(()=>d(l),300);return()=>clearTimeout(q)},[l,s]);const f=e?ie(e):[],h=x.useWatch({control:n,name:f.length>0?f:[]}),p={};f.length>0&&f.forEach((q,k)=>{p[q]=Array.isArray(h)?h[k]:h});const v=e?se(e,p,s?o:void 0):"",b=e?oe(f,p):!1,{data:j,isLoading:S,error:w}=qe.useQuery({queryKey:["options",v],queryFn:async()=>a.get(v),enabled:!!e&&b,staleTime:300*1e3,retry:2});y.useEffect(()=>{w&&i.onError&&i.onError(`Failed to load options: ${w.message}`)},[w,i]);const D=y.useMemo(()=>{if(!e)return t??[];const q=j?j.map(E=>({value:E[e.valueKey??"value"],label:E[e.labelKey??"label"]})):[];if(!r?.length)return q;const k=new Set(q.map(E=>String(E.value)));return[...r.filter(E=>!k.has(String(E.value))),...q]},[j,r,e,t]),C=y.useCallback((q,k)=>{c(k)},[]);return{options:D,isLoading:S,error:w,inputValue:s?l:void 0,onInputChange:s?C:void 0}}function ue(e,t){const{apiClient:r}=R(),n=y.useRef(void 0),a=y.useRef(null),i=y.useCallback(async c=>{if(!t||!c||typeof c=="string"&&c.trim()==="")return!0;a.current&&a.current.abort();const o=new AbortController;a.current=o;try{const d=new URL(t.apiUrl,window.location.origin);if(t.queryParams)for(const p of t.queryParams)(p.dependsOn===e||!p.dependsOn)&&d.searchParams.set(p.key,String(c));else d.searchParams.set(e,String(c));const f=await r.get(d.pathname+d.search,{signal:o.signal});return f.valid??!f.exists?!0:t.message||"Validation failed"}catch(d){return d instanceof Error&&d.name==="AbortError"?!0:"⚠️ Validation unavailable (API error)"}},[t,e,r]),s=y.useCallback(c=>new Promise(o=>{n.current&&clearTimeout(n.current),n.current=setTimeout(async()=>{const d=await i(c);o(d)},t?.debounce??500)}),[i,t?.debounce]),l=y.useCallback(async c=>(n.current&&clearTimeout(n.current),i(c)),[i]);return{validateDebounced:s,validateImmediate:l}}const Y=(e,t,r)=>{if(e&&"reportValidity"in e){const n=x.get(r,t);e.setCustomValidity(n&&n.message||""),e.reportValidity()}},Z=(e,t)=>{for(const r in t.fields){const n=t.fields[r];n&&n.ref&&"reportValidity"in n.ref?Y(n.ref,r,e):n&&n.refs&&n.refs.forEach(a=>Y(a,r,e))}},J=(e,t)=>{t.shouldUseNativeValidation&&Z(e,t);const r={};for(const n in e){const a=x.get(t.fields,n),i=Object.assign(e[n]||{},{ref:a&&a.ref});if(Ke(t.names||Object.keys(e),n)){const s=Object.assign({},x.get(r,n));x.set(s,"root",i),x.set(r,n,s)}else x.set(r,n,i)}return r},Ke=(e,t)=>{const r=X(t);return e.some(n=>X(n).match(`^${r}\\.\\d+`))};function X(e){return e.replace(/\]|\[/g,"")}function ce(e,t,r){function n(l,c){if(l._zod||Object.defineProperty(l,"_zod",{value:{def:c,constr:s,traits:new Set},enumerable:!1}),l._zod.traits.has(e))return;l._zod.traits.add(e),t(l,c);const o=s.prototype,d=Object.keys(o);for(let f=0;f<d.length;f++){const h=d[f];h in l||(l[h]=o[h].bind(l))}}const a=r?.Parent??Object;class i extends a{}Object.defineProperty(i,"name",{value:e});function s(l){var c;const o=r?.Parent?new i:this;n(o,l),(c=o._zod).deferred??(c.deferred=[]);for(const d of o._zod.deferred)d();return o}return Object.defineProperty(s,"init",{value:n}),Object.defineProperty(s,Symbol.hasInstance,{value:l=>r?.Parent&&l instanceof r.Parent?!0:l?._zod?.traits?.has(e)}),Object.defineProperty(s,"name",{value:e}),s}class Ze extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}const He={};function de(e){return He}function Ge(e,t){return typeof t=="bigint"?t.toString():t}const fe="captureStackTrace"in Error?Error.captureStackTrace:(...e)=>{};function B(e){return typeof e=="string"?e:e?.message}function me(e,t,r){const n={...e,path:e.path??[]};if(!e.message){const a=B(e.inst?._zod.def?.error?.(e))??B(t?.error?.(e))??B(r.customError?.(e))??B(r.localeError?.(e))??"Invalid input";n.message=a}return delete n.inst,delete n.continue,t?.reportInput||delete n.input,n}const pe=(e,t)=>{e.name="$ZodError",Object.defineProperty(e,"_zod",{value:e._zod,enumerable:!1}),Object.defineProperty(e,"issues",{value:t,enumerable:!1}),e.message=JSON.stringify(t,Ge,2),Object.defineProperty(e,"toString",{value:()=>e.message,enumerable:!1})},Qe=ce("$ZodError",pe),he=ce("$ZodError",pe,{Parent:Error}),Ye=e=>(t,r,n,a)=>{const i=n?Object.assign(n,{async:!1}):{async:!1},s=t._zod.run({value:r,issues:[]},i);if(s instanceof Promise)throw new Ze;if(s.issues.length){const l=new(a?.Err??e)(s.issues.map(c=>me(c,i,de())));throw fe(l,a?.callee),l}return s.value},Je=Ye(he),Xe=e=>async(t,r,n,a)=>{const i=n?Object.assign(n,{async:!0}):{async:!0};let s=t._zod.run({value:r,issues:[]},i);if(s instanceof Promise&&(s=await s),s.issues.length){const l=new(a?.Err??e)(s.issues.map(c=>me(c,i,de())));throw fe(l,a?.callee),l}return s.value},et=Xe(he);function ee(e,t){try{var r=e()}catch(n){return t(n)}return r&&r.then?r.then(void 0,t):r}function tt(e,t){for(var r={};e.length;){var n=e[0],a=n.code,i=n.message,s=n.path.join(".");if(!r[s])if("unionErrors"in n){var l=n.unionErrors[0].errors[0];r[s]={message:l.message,type:l.code}}else r[s]={message:i,type:a};if("unionErrors"in n&&n.unionErrors.forEach(function(d){return d.errors.forEach(function(f){return e.push(f)})}),t){var c=r[s].types,o=c&&c[n.code];r[s]=x.appendErrors(s,t,r,a,o?[].concat(o,n.message):n.message)}e.shift()}return r}function rt(e,t){for(var r={};e.length;){var n=e[0],a=n.code,i=n.message,s=n.path.join(".");if(!r[s])if(n.code==="invalid_union"&&n.errors.length>0){var l=n.errors[0][0];r[s]={message:l.message,type:l.code}}else r[s]={message:i,type:a};if(n.code==="invalid_union"&&n.errors.forEach(function(d){return d.forEach(function(f){return e.push(f)})}),t){var c=r[s].types,o=c&&c[n.code];r[s]=x.appendErrors(s,t,r,a,o?[].concat(o,n.message):n.message)}e.shift()}return r}function nt(e,t,r){if(r===void 0&&(r={}),(function(n){return"_def"in n&&typeof n._def=="object"&&"typeName"in n._def})(e))return function(n,a,i){try{return Promise.resolve(ee(function(){return Promise.resolve(e[r.mode==="sync"?"parse":"parseAsync"](n,t)).then(function(s){return i.shouldUseNativeValidation&&Z({},i),{errors:{},values:r.raw?Object.assign({},n):s}})},function(s){if((function(l){return Array.isArray(l?.issues)})(s))return{values:{},errors:J(tt(s.errors,!i.shouldUseNativeValidation&&i.criteriaMode==="all"),i)};throw s}))}catch(s){return Promise.reject(s)}};if((function(n){return"_zod"in n&&typeof n._zod=="object"})(e))return function(n,a,i){try{return Promise.resolve(ee(function(){return Promise.resolve((r.mode==="sync"?Je:et)(e,n,t)).then(function(s){return i.shouldUseNativeValidation&&Z({},i),{errors:{},values:r.raw?Object.assign({},n):s}})},function(s){if((function(l){return l instanceof Qe})(s))return{values:{},errors:J(rt(s.issues,!i.shouldUseNativeValidation&&i.criteriaMode==="all"),i)};throw s}))}catch(s){return Promise.reject(s)}};throw new Error("Invalid input: not a Zod schema")}function st(e){return y.useCallback(async(t,r,n)=>{const a=new Set;for(const[o,d]of Object.entries(e.fields))(!d.showIf||A(d.showIf,t))&&a.add(o);const i=Object.entries(e.fields).filter(([o])=>a.has(o)).reduce((o,[d,f])=>(o[d]=f,o),{}),s=ne({...e,fields:i});return await nt(s)(t,r,n)},[e])}const xe=y.createContext(null);function it(){return y.useContext(xe)}function U(e,t){const r=it();y.useEffect(()=>(r?.register(e,t),()=>{r?.unregister(e)}),[e,t,r])}function _({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.TextField,l=t,{validateDebounced:c,validateImmediate:o}=ue(e,l.validation?.asyncValidation);if(t.type!=="text"&&t.type!=="password"&&t.type!=="email"&&t.type!=="textarea")return null;const d=t.type==="textarea",f=d?t.rows||4:void 0,h=!!(l.validation?.required||l.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,rules:{validate:{asyncValidation:async p=>c(p)}},render:({field:{value:p,onChange:v,onBlur:b,ref:j},fieldState:{error:S}})=>u.jsx(s,{value:p||"",onChange:v,onBlur:async()=>{b(),l.validation?.asyncValidation&&await o(p)},inputRef:j,type:t.type==="textarea"?"text":t.type,label:l.label,required:h,placeholder:l.placeholder,helperText:S?.message||l.helperText,error:!!S,disabled:r||l.disabled,autoComplete:l.autoComplete,multiline:d,rows:f,size:i})})}function ot({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.NumberField;if(t.type!=="number")return null;const l=!!(t.validation?.required||t.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,onBlur:d,name:f},fieldState:{error:h}})=>u.jsx(s,{name:f,value:c,onValueChange:p=>o(p),onBlur:d,label:t.label,required:l,placeholder:t.placeholder,helperText:h?.message||t.helperText,error:!!h,disabled:r||t.disabled,min:t.min,max:t.max,step:t.step,size:i})})}function at({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.Checkbox;if(t.type!=="checkbox")return null;const l=!!(t.validation?.required||t.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,...d},fieldState:{error:f}})=>u.jsx(s,{...d,label:t.label,required:l,checked:!!c,onChange:h=>o(h.target.checked),disabled:r||t.disabled,error:!!f,helperText:f?.message||t.helperText,size:i})})}function lt({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.Switch;if(t.type!=="switch")return null;const l=!!(t.validation?.required||t.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,...d},fieldState:{error:f}})=>u.jsx(s,{...d,label:t.label,required:l,checked:!!c,onChange:h=>o(h.target.checked),disabled:r||t.disabled,error:!!f,helperText:f?.message||t.helperText,size:i})})}function ut({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.Select,o=t,{options:d,isLoading:f}=O(t.type==="select"?o.optionsSource:void 0,t.type==="select"?o.options:void 0);if(U(e,d),y.useEffect(()=>{if(t.type!=="select")return;const p=i(e);p&&!d.some(v=>v.value===p)&&a(e,"",{shouldValidate:!1})},[d,e,a,i,t.type]),t.type!=="select")return null;const h=!!(o.validation?.required||o.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:p,onChange:v,onBlur:b,ref:j},fieldState:{error:S}})=>u.jsxs(m.FormControl,{fullWidth:!0,error:!!S,disabled:r||o.disabled||f,size:l,children:[o.label&&u.jsx(m.InputLabel,{required:h,children:o.label}),u.jsx(c,{name:e,value:p||"",onChange:v,onBlur:b,label:o.label,options:d,size:l,ref:j}),(S?.message||o.helperText)&&u.jsx(m.FormHelperText,{children:S?.message||o.helperText})]})})}function ct({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.Autocomplete,o=t,{options:d,isLoading:f,inputValue:h,onInputChange:p}=O(t.type==="multiselect"?o.optionsSource:void 0,t.type==="multiselect"?o.options:void 0,t.type==="multiselect"?o.initialOptions:void 0);if(U(e,d),y.useEffect(()=>{if(t.type!=="multiselect"||f||p)return;const b=i(e);if(Array.isArray(b)&&b.length>0){const j=b.filter(S=>d.some(w=>w.value===S));j.length!==b.length&&a(e,j,{shouldValidate:!1})}},[d,f,p,e,a,i,t.type]),t.type!=="multiselect")return null;const v=!!(o.validation?.required||o.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:b,onChange:j,...S},fieldState:{error:w}})=>u.jsx(c,{...S,multiple:!0,options:d,value:Array.isArray(b)?d.filter(D=>b.includes(D.value)):[],onChange:(D,C)=>{const q=C.map(k=>k.value);j(q)},isOptionEqualToValue:(D,C)=>D.value===C.value,label:o.label,required:v,placeholder:o.placeholder,error:!!w,helperText:w?.message||o.helperText,disabled:r||o.disabled||f&&!p,loading:f,size:l,inputValue:h,onInputChange:p,filterOptions:p?D=>D:void 0})})}function dt({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.Autocomplete,o=t,{options:d,isLoading:f,inputValue:h,onInputChange:p}=O(t.type==="autocomplete"?o.optionsSource:void 0,t.type==="autocomplete"?o.options:void 0,t.type==="autocomplete"?o.initialOptions:void 0);if(U(e,d),y.useEffect(()=>{if(t.type!=="autocomplete"||f||p)return;const b=i(e);b&&!d.some(j=>j.value===b)&&a(e,"",{shouldValidate:!1})},[d,f,p,e,a,i,t.type]),t.type!=="autocomplete")return null;const v=!!(o.validation?.required||o.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:b,onChange:j,...S},fieldState:{error:w}})=>u.jsx(c,{...S,options:d,value:d.find(D=>D.value===b)??null,onChange:(D,C)=>{j(C?.value??"")},isOptionEqualToValue:(D,C)=>D.value===C.value,label:o.label,required:v,placeholder:o.placeholder,error:!!w,helperText:w?.message||o.helperText,disabled:r||o.disabled||f&&!p,loading:f,size:l,inputValue:h,onInputChange:p,filterOptions:p?D=>D:void 0})})}function ft({name:e,config:t,disabled:r}){const{control:n,setValue:a,getValues:i}=x.useFormContext(),{components:s,size:l}=R(),c=s.RadioGroup,o=t,{options:d,isLoading:f}=O(t.type==="radio"?o.optionsSource:void 0,t.type==="radio"?o.options:void 0);U(e,d);const h=d.map(v=>({label:v.label,value:String(v.value)}));if(y.useEffect(()=>{if(t.type!=="radio")return;const v=i(e);v&&!d.some(b=>String(b.value)===String(v))&&a(e,"",{shouldValidate:!1})},[d,e,a,i,t.type]),t.type!=="radio")return null;const p=!!(o.validation?.required||o.validation?.requiredIf);return f?u.jsx(m.Skeleton,{variant:"rectangular",height:100}):u.jsx(x.Controller,{name:e,control:n,render:({field:v,fieldState:{error:b}})=>u.jsx(c,{...v,label:o.label,required:p,options:h,row:o.direction==="row",error:!!b,helperText:b?.message||o.helperText,disabled:r||o.disabled,size:l})})}function mt({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.DatePicker,l=t;if(t.type!=="date")return null;const c=!!(l.validation?.required||l.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:o,onChange:d},fieldState:{error:f}})=>u.jsx(s,{label:l.label,required:c,includeTime:l.includeTime,format:l.format,value:o||null,onChange:d,disabled:r||l.disabled,minDate:l.validation?.minDate,maxDate:l.validation?.maxDate,error:!!f,helperText:f?.message||l.helperText,size:i})})}function pt({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a,size:i}=R(),s=a.DateRangePicker,l=t;if(t.type!=="dateRange")return null;const c=!!(l.validation?.required||l.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:o,onChange:d},fieldState:{error:f}})=>u.jsx(s,{label:l.label,required:c,includeTime:l.includeTime,format:l.format,value:o||{start:null,end:null},onChange:d,disabled:r||l.disabled,minDate:l.validation?.minDate,maxDate:l.validation?.maxDate,error:!!f,helperText:f?.message||l.helperText,size:i})})}function ht({name:e,config:t,disabled:r}){const{control:n}=x.useFormContext(),{components:a}=R(),i=a.FileUpload,s=t;if(t.type!=="file")return null;const l=!!(s.validation?.required||s.validation?.requiredIf);return u.jsx(x.Controller,{name:e,control:n,render:({field:{value:c,onChange:o,onBlur:d,ref:f},fieldState:{error:h}})=>u.jsx(i,{label:s.label,required:l,value:c||null,onChange:o,onBlur:d,disabled:r||s.disabled,multiple:s.multiple,accept:s.accept,error:!!h,helperText:h?.message||s.helperText,ref:f})})}const ge={text:_,password:_,email:_,textarea:_,number:ot,checkbox:at,switch:lt,select:ut,multiselect:ct,autocomplete:dt,radio:ft,date:mt,dateRange:pt,file:ht};function xt(e){return ge[e]??null}function gt(e,t){ge[e]=t}function yt({name:e,config:t,disabled:r}){const n=xt(t.type);return n?u.jsx(n,{name:e,config:t,disabled:r}):(console.warn(`Unknown field type: ${t.type}`),u.jsxs(m.Alert,{severity:"warning",sx:{my:1},children:["Unknown field type: ",u.jsx("code",{children:t.type})]}))}function bt({section:e,fields:t,disabled:r,visibleFields:n,slotProps:a}){const i=e.fields.filter(s=>n.has(s));return i.length===0?null:u.jsxs(m.Box,{sx:V({mb:4},a?.root?.sx),children:[e.title&&u.jsx(m.Typography,{variant:"h6",sx:V({mb:2},a?.title?.sx),children:e.title}),e.description&&u.jsx(m.Typography,{variant:"body2",color:"text.secondary",sx:V({mb:2},a?.description?.sx),children:e.description}),u.jsx(m.Box,{sx:V({display:"flex",flexDirection:"column",gap:2},a?.content?.sx),children:i.map(s=>u.jsx(yt,{name:s,config:t[s],disabled:r},s))})]})}class ye extends y.Component{constructor(t){super(t),this.handleReset=()=>{this.setState({hasError:!1,error:null})},this.state={hasError:!1,error:null}}static getDerivedStateFromError(t){return{hasError:!0,error:t}}componentDidCatch(t,r){console.error("DynamicForm Error:",t,r),this.props.onError?.(t,r)}render(){return this.state.hasError?this.props.fallback?this.props.fallback:u.jsxs(m.Box,{sx:{p:3},children:[u.jsxs(m.Alert,{severity:"error",sx:{mb:2},children:[u.jsx(m.Typography,{variant:"h6",gutterBottom:!0,children:"Form Error"}),u.jsx(m.Typography,{variant:"body2",sx:{mb:2},children:"Something went wrong while rendering the form."}),this.state.error&&u.jsx(m.Typography,{variant:"caption",sx:{fontFamily:"monospace"},children:this.state.error.message})]}),u.jsx(m.Button,{variant:"outlined",onClick:this.handleReset,children:"Reset Form"})]}):this.props.children}}function vt({schema:e,mode:t,sectionSlotProps:r,children:n,formState:a}){const i=le(e),s=t==="disabled",l=new Map(e.sections.map(d=>[d.id,d])),c=d=>{const f=l.get(d);return f?u.jsx(bt,{section:f,fields:e.fields,disabled:s,visibleFields:i,slotProps:r},f.id):null},o=(d,f)=>{const h=d.columns??d.sections.length;return h<=1?u.jsx(m.Box,{children:d.sections.map(c)},f):u.jsx(m.Box,{sx:{display:"grid",gridTemplateColumns:{xs:"1fr",md:`repeat(${h}, 1fr)`},gap:3,alignItems:"start"},children:d.sections.map(c)},f)};return u.jsxs(u.Fragment,{children:[e.layout?e.layout.map(o):e.sections.map(d=>c(d.id)),typeof n=="function"?n(a):n]})}function Ft({schema:e,onSubmit:t,mode:r="edit",defaultValues:n,apiClient:a,size:i,sectionSlotProps:s,children:l}){const c=R(),o=!!(a||i!==void 0),d=y.useMemo(()=>o?{...c,...a&&{apiClient:a},...i!==void 0&&{size:i}}:c,[c,a,i,o]),f=y.useMemo(()=>We(c.sectionSlotProps,s),[c.sectionSlotProps,s]),h=y.useRef(new Map),p=y.useMemo(()=>({register:(C,q)=>{h.current.set(C,q)},unregister:C=>{h.current.delete(C)},buildLabelMap:()=>{const C={};for(const[q,k]of h.current.entries()){if(k.length===0)continue;const P={};for(const E of k)P[String(E.value)]=E.label;C[q]=P}return C}}),[]),v=y.useCallback(C=>t(C,p.buildLabelMap()),[t,p]),b=st(e),j=ae(e),S=x.useForm({resolver:b,defaultValues:{...j,...n},mode:"onChange",reValidateMode:"onChange"}),w={isValid:S.formState.isValid,isSubmitting:S.formState.isSubmitting},D=u.jsx(ye,{children:u.jsx(x.FormProvider,{...S,children:u.jsx(xe,{value:p,children:u.jsx(m.Box,{component:"form",onSubmit:S.handleSubmit(v,C=>{console.error("❌ Form validation errors:",C)}),noValidate:!0,children:u.jsx(vt,{schema:e,mode:r,sectionSlotProps:f,children:l,formState:w})})})})});return o?u.jsx(H,{value:d,children:D}):D}exports.DynamicForm=Ft;exports.DynamicFormsProvider=Le;exports.FormErrorBoundary=ye;exports.areDependenciesFilled=oe;exports.buildOptionsUrl=se;exports.buildZodSchema=ne;exports.evaluateCondition=A;exports.extractDefaultValues=ae;exports.extractOptionsDependencies=ie;exports.formatFileSize=re;exports.isEmpty=M;exports.registerFieldComponent=gt;exports.useAsyncValidation=ue;exports.useDynamicFormsContext=R;exports.useFieldVisibility=le;exports.useOptionsSource=O;
|