dynamic-formik-form 1.0.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.
- package/README.md +810 -0
- package/dist/index.d.mts +952 -0
- package/dist/index.d.ts +952 -0
- package/dist/index.js +3666 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3611 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,810 @@
|
|
|
1
|
+
# dynamic-formik-form
|
|
2
|
+
|
|
3
|
+
> **A production-ready, adapter-based React form library that generates dynamic forms from JSON configuration using Formik and Yup. Works with Material-UI, Bootstrap, Ant Design, or plain CSS.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/dynamic-formik-form)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
## Why This Library?
|
|
10
|
+
|
|
11
|
+
Building forms in React is repetitive and error-prone. `dynamic-formik-form` solves this by:
|
|
12
|
+
|
|
13
|
+
- **π― JSON-Driven Forms**: Define forms as data, not code. Perfect for admin panels, CMSs, and dynamic UIs
|
|
14
|
+
- **π¨ UI Library Agnostic**: Use Material-UI, Bootstrap, Ant Design, or your own CSSβswitch adapters without changing form logic
|
|
15
|
+
- **β
Built on Battle-Tested Libraries**: Leverages Formik for state management and Yup for validation
|
|
16
|
+
- **π§ Production-Ready**: Extracted from production code, handling edge cases like conditional fields, nested arrays, and complex validation
|
|
17
|
+
- **π¦ Zero Bloat**: Tree-shakeable, modular architecture. Only bundle what you use
|
|
18
|
+
- **π Fully Extensible**: Create custom adapters for any UI library or design system
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- π¨ **Extensible UI Libraries** - Material-UI, Bootstrap, Ant Design, or plain CSS
|
|
23
|
+
- π― **22 Field Types** - Input, textarea, select, checkbox, radio, date picker, file upload, and more
|
|
24
|
+
- π **Icon Flexibility** - Use any icon library (default SVG icons included)
|
|
25
|
+
- π¦ **TypeScript First** - Full TypeScript support with comprehensive types
|
|
26
|
+
- π§© **Modular Architecture** - Small, focused, reusable components
|
|
27
|
+
- β
**Production Ready** - Extracted from production code, battle-tested
|
|
28
|
+
- π **Conditional Fields** - Show/hide fields based on form values
|
|
29
|
+
- π **Field Arrays** - Dynamic key-value pairs and simple arrays
|
|
30
|
+
- ποΈ **Nested Attributes** - Complex nested form structures
|
|
31
|
+
- π **Async Select** - Load options from API endpoints
|
|
32
|
+
- π¨ **Custom Components** - Inject custom React components anywhere
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
### Core Package
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install dynamic-formik-form formik yup react react-dom
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Peer Dependencies
|
|
43
|
+
|
|
44
|
+
The library requires these peer dependencies (you control the versions):
|
|
45
|
+
|
|
46
|
+
- `react` >= 18.0.0
|
|
47
|
+
- `react-dom` >= 18.0.0
|
|
48
|
+
- `formik` ^2.0.0 || ^3.0.0
|
|
49
|
+
- `yup` ^1.0.0
|
|
50
|
+
|
|
51
|
+
### UI Library Adapters (Optional)
|
|
52
|
+
|
|
53
|
+
Choose one or more UI libraries:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Material-UI
|
|
57
|
+
npm install @mui/material @mui/x-date-pickers @mui/icons-material
|
|
58
|
+
|
|
59
|
+
# Bootstrap React
|
|
60
|
+
npm install react-bootstrap react-bootstrap-icons bootstrap
|
|
61
|
+
|
|
62
|
+
# Ant Design
|
|
63
|
+
npm install antd @ant-design/icons
|
|
64
|
+
|
|
65
|
+
# Or use plain CSS - no additional dependencies needed!
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
### Minimal Example
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import React from 'react';
|
|
74
|
+
import { useFormik } from 'formik';
|
|
75
|
+
import * as Yup from 'yup';
|
|
76
|
+
import { DynamicForm } from 'dynamic-formik-form';
|
|
77
|
+
|
|
78
|
+
const MyForm = () => {
|
|
79
|
+
const formik = useFormik({
|
|
80
|
+
initialValues: {
|
|
81
|
+
email: '',
|
|
82
|
+
password: '',
|
|
83
|
+
},
|
|
84
|
+
validationSchema: Yup.object({
|
|
85
|
+
email: Yup.string().email('Invalid email').required('Email is required'),
|
|
86
|
+
password: Yup.string().min(8, 'Password must be at least 8 characters').required(),
|
|
87
|
+
}),
|
|
88
|
+
onSubmit: (values) => {
|
|
89
|
+
console.log('Form submitted:', values);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const fields = [
|
|
94
|
+
{
|
|
95
|
+
type: 'inputfield' as const,
|
|
96
|
+
name: 'email',
|
|
97
|
+
label: 'Email Address',
|
|
98
|
+
placeholder: 'Enter your email',
|
|
99
|
+
required: true,
|
|
100
|
+
fieldType: 'email',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'inputfield' as const,
|
|
104
|
+
name: 'password',
|
|
105
|
+
label: 'Password',
|
|
106
|
+
fieldType: 'password',
|
|
107
|
+
required: true,
|
|
108
|
+
isVisibleEnable: true, // Show password visibility toggle
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<form onSubmit={formik.handleSubmit}>
|
|
114
|
+
<DynamicForm fields={fields} formik={formik} />
|
|
115
|
+
<button type="submit">Submit</button>
|
|
116
|
+
</form>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### JSON-Driven Form Example
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Define form structure as JSON (can come from API, config file, etc.)
|
|
125
|
+
const formConfig = [
|
|
126
|
+
{
|
|
127
|
+
type: 'inputfield',
|
|
128
|
+
name: 'username',
|
|
129
|
+
label: 'Username',
|
|
130
|
+
required: true,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'dropdown',
|
|
134
|
+
name: 'country',
|
|
135
|
+
label: 'Country',
|
|
136
|
+
values: {
|
|
137
|
+
'': 'Select a country',
|
|
138
|
+
us: 'United States',
|
|
139
|
+
uk: 'United Kingdom',
|
|
140
|
+
ca: 'Canada',
|
|
141
|
+
},
|
|
142
|
+
required: true,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'textarea',
|
|
146
|
+
name: 'comments',
|
|
147
|
+
label: 'Comments',
|
|
148
|
+
rows: 4,
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
// Render the form
|
|
153
|
+
<DynamicForm fields={formConfig} formik={formik} />
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## UI Library Adapters
|
|
157
|
+
|
|
158
|
+
### With Material-UI
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { DynamicForm, createMUIAdapter, createMUIIcons } from 'dynamic-formik-form';
|
|
162
|
+
import {
|
|
163
|
+
Box,
|
|
164
|
+
Button,
|
|
165
|
+
TextField,
|
|
166
|
+
Checkbox,
|
|
167
|
+
FormControlLabel,
|
|
168
|
+
FormHelperText,
|
|
169
|
+
Radio,
|
|
170
|
+
RadioGroup,
|
|
171
|
+
Switch,
|
|
172
|
+
Typography,
|
|
173
|
+
Popover,
|
|
174
|
+
MenuItem,
|
|
175
|
+
Paper,
|
|
176
|
+
IconButton,
|
|
177
|
+
} from '@mui/material';
|
|
178
|
+
import { DatePicker } from '@mui/x-date-pickers';
|
|
179
|
+
import {
|
|
180
|
+
Info as InfoIcon,
|
|
181
|
+
Visibility as VisibilityIcon,
|
|
182
|
+
VisibilityOff as VisibilityOffIcon,
|
|
183
|
+
ContentCopy as CopyIcon,
|
|
184
|
+
Add as AddIcon,
|
|
185
|
+
Delete as DeleteIcon,
|
|
186
|
+
} from '@mui/icons-material';
|
|
187
|
+
|
|
188
|
+
const muiAdapter = createMUIAdapter(
|
|
189
|
+
Box,
|
|
190
|
+
Button,
|
|
191
|
+
Checkbox,
|
|
192
|
+
FormControlLabel,
|
|
193
|
+
FormHelperText,
|
|
194
|
+
IconButton,
|
|
195
|
+
MenuItem,
|
|
196
|
+
Paper,
|
|
197
|
+
Popover,
|
|
198
|
+
Radio,
|
|
199
|
+
RadioGroup,
|
|
200
|
+
Switch,
|
|
201
|
+
TextField,
|
|
202
|
+
Typography,
|
|
203
|
+
DatePicker
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const muiIcons = createMUIIcons(
|
|
207
|
+
InfoIcon,
|
|
208
|
+
VisibilityIcon,
|
|
209
|
+
VisibilityOffIcon,
|
|
210
|
+
CopyIcon,
|
|
211
|
+
AddIcon,
|
|
212
|
+
DeleteIcon
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
<DynamicForm
|
|
216
|
+
fields={fields}
|
|
217
|
+
formik={formik}
|
|
218
|
+
uiLibrary={{
|
|
219
|
+
adapter: muiAdapter,
|
|
220
|
+
icons: muiIcons,
|
|
221
|
+
name: 'mui',
|
|
222
|
+
}}
|
|
223
|
+
/>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### With Bootstrap
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { DynamicForm, createBootstrapAdapter, createBootstrapIcons } from 'dynamic-formik-form';
|
|
230
|
+
import {
|
|
231
|
+
Form,
|
|
232
|
+
FormControl,
|
|
233
|
+
FormLabel,
|
|
234
|
+
FormText,
|
|
235
|
+
FormCheck,
|
|
236
|
+
FormSelect,
|
|
237
|
+
Button,
|
|
238
|
+
InputGroup,
|
|
239
|
+
OverlayTrigger,
|
|
240
|
+
Popover,
|
|
241
|
+
ListGroup,
|
|
242
|
+
ListGroupItem,
|
|
243
|
+
} from 'react-bootstrap';
|
|
244
|
+
import {
|
|
245
|
+
InfoCircle as InfoIcon,
|
|
246
|
+
Eye as VisibilityIcon,
|
|
247
|
+
EyeSlash as VisibilityOffIcon,
|
|
248
|
+
Clipboard as CopyIcon,
|
|
249
|
+
PlusCircle as AddIcon,
|
|
250
|
+
Trash as DeleteIcon,
|
|
251
|
+
} from 'react-bootstrap-icons';
|
|
252
|
+
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
253
|
+
|
|
254
|
+
const bootstrapAdapter = createBootstrapAdapter(
|
|
255
|
+
Form,
|
|
256
|
+
FormControl,
|
|
257
|
+
FormLabel,
|
|
258
|
+
FormText,
|
|
259
|
+
FormCheck,
|
|
260
|
+
FormSelect,
|
|
261
|
+
Button,
|
|
262
|
+
InputGroup,
|
|
263
|
+
OverlayTrigger,
|
|
264
|
+
Popover,
|
|
265
|
+
ListGroup,
|
|
266
|
+
ListGroupItem
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const bootstrapIcons = createBootstrapIcons(
|
|
270
|
+
InfoIcon,
|
|
271
|
+
VisibilityIcon,
|
|
272
|
+
VisibilityOffIcon,
|
|
273
|
+
CopyIcon,
|
|
274
|
+
AddIcon,
|
|
275
|
+
DeleteIcon
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
<DynamicForm
|
|
279
|
+
fields={fields}
|
|
280
|
+
formik={formik}
|
|
281
|
+
uiLibrary={{
|
|
282
|
+
adapter: bootstrapAdapter,
|
|
283
|
+
icons: bootstrapIcons,
|
|
284
|
+
name: 'bootstrap',
|
|
285
|
+
}}
|
|
286
|
+
/>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### With Ant Design
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { DynamicForm, createAntDesignAdapter, createAntDesignIcons } from 'dynamic-formik-form';
|
|
293
|
+
import {
|
|
294
|
+
Input,
|
|
295
|
+
Button,
|
|
296
|
+
Checkbox,
|
|
297
|
+
Radio,
|
|
298
|
+
Switch,
|
|
299
|
+
Select,
|
|
300
|
+
Form,
|
|
301
|
+
Typography,
|
|
302
|
+
Popover,
|
|
303
|
+
DatePicker,
|
|
304
|
+
} from 'antd';
|
|
305
|
+
import {
|
|
306
|
+
InfoCircleOutlined as InfoIcon,
|
|
307
|
+
EyeOutlined as VisibilityIcon,
|
|
308
|
+
EyeInvisibleOutlined as VisibilityOffIcon,
|
|
309
|
+
CopyOutlined as CopyIcon,
|
|
310
|
+
PlusCircleOutlined as AddIcon,
|
|
311
|
+
DeleteOutlined as DeleteIcon,
|
|
312
|
+
} from '@ant-design/icons';
|
|
313
|
+
|
|
314
|
+
const antdAdapter = createAntDesignAdapter(
|
|
315
|
+
Input,
|
|
316
|
+
Button,
|
|
317
|
+
Checkbox,
|
|
318
|
+
Radio,
|
|
319
|
+
Switch,
|
|
320
|
+
Select,
|
|
321
|
+
Form,
|
|
322
|
+
Typography,
|
|
323
|
+
Popover,
|
|
324
|
+
DatePicker
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const antdIcons = createAntDesignIcons(
|
|
328
|
+
InfoIcon,
|
|
329
|
+
VisibilityIcon,
|
|
330
|
+
VisibilityOffIcon,
|
|
331
|
+
CopyIcon,
|
|
332
|
+
AddIcon,
|
|
333
|
+
DeleteIcon
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
<DynamicForm
|
|
337
|
+
fields={fields}
|
|
338
|
+
formik={formik}
|
|
339
|
+
uiLibrary={{
|
|
340
|
+
adapter: antdAdapter,
|
|
341
|
+
icons: antdIcons,
|
|
342
|
+
name: 'antd',
|
|
343
|
+
}}
|
|
344
|
+
/>
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### With Plain CSS (No UI Library)
|
|
348
|
+
|
|
349
|
+
The library works out-of-the-box with native HTML elements. Just style them with your own CSS!
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { DynamicForm } from 'dynamic-formik-form';
|
|
353
|
+
import './my-custom-styles.css';
|
|
354
|
+
|
|
355
|
+
// No uiLibrary prop needed - uses default HTML adapter
|
|
356
|
+
<DynamicForm fields={fields} formik={formik} />
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
```css
|
|
360
|
+
/* my-custom-styles.css */
|
|
361
|
+
input[type="text"],
|
|
362
|
+
input[type="email"],
|
|
363
|
+
input[type="password"] {
|
|
364
|
+
padding: 8px 12px;
|
|
365
|
+
border: 1px solid #ccc;
|
|
366
|
+
border-radius: 4px;
|
|
367
|
+
font-size: 14px;
|
|
368
|
+
width: 100%;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
input.error {
|
|
372
|
+
border-color: #dc3545;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.form-control {
|
|
376
|
+
margin-bottom: 16px;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
label {
|
|
380
|
+
display: block;
|
|
381
|
+
margin-bottom: 4px;
|
|
382
|
+
font-weight: 500;
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## API Reference
|
|
387
|
+
|
|
388
|
+
### `<DynamicForm />`
|
|
389
|
+
|
|
390
|
+
The main component that renders fields dynamically based on configuration.
|
|
391
|
+
|
|
392
|
+
#### Props
|
|
393
|
+
|
|
394
|
+
| Prop Name | Type | Required | Default | Description |
|
|
395
|
+
|-----------|------|----------|---------|-------------|
|
|
396
|
+
| `fields` | `(FieldConfig \| false)[]` | Yes | - | Array of field configurations. Use `false` for conditional fields. |
|
|
397
|
+
| `formik` | `FormikProps<T>` | Yes | - | Formik instance from `useFormik()` hook. Must include `values`, `errors`, `touched`, `handleChange`, `handleBlur`, `setFieldValue`, etc. |
|
|
398
|
+
| `uiLibrary` | `UILibraryConfig` | No | `{ adapter: defaultAdapter, icons: defaultIcons }` | UI library adapter and icons configuration. See [Adapter Configuration](#adapter-configuration). |
|
|
399
|
+
| `firstInitialValues` | `T` | No | - | Initial values for the form. Used for resetting child fields. |
|
|
400
|
+
| `customFormChange` | `(event: ChangeEvent, type: string) => void` | No | - | Custom change handler that receives the event and field type. Overrides default Formik handlers. |
|
|
401
|
+
| `fieldCount` | `number` | No | - | Total number of fields. Used for field array management. |
|
|
402
|
+
| `attributeFields` | `FieldConfig[]` | No | - | Additional fields for attribute field type. |
|
|
403
|
+
| `onUpdateFormFields` | `(fields: FieldConfig[]) => void` | No | - | Callback when fields are updated dynamically. |
|
|
404
|
+
| `handleDeleteChange` | `(count: number) => void` | No | - | Callback when field array items are deleted. |
|
|
405
|
+
| `RadiusTab` | `boolean` | No | `false` | Enable rounded tab styling for attribute fields. |
|
|
406
|
+
| `value` | `number` | No | - | Tab index value for attribute fields. |
|
|
407
|
+
|
|
408
|
+
#### Example
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
<DynamicForm
|
|
412
|
+
fields={fields}
|
|
413
|
+
formik={formik}
|
|
414
|
+
uiLibrary={{
|
|
415
|
+
adapter: muiAdapter,
|
|
416
|
+
icons: muiIcons,
|
|
417
|
+
name: 'mui',
|
|
418
|
+
}}
|
|
419
|
+
customFormChange={(event, type) => {
|
|
420
|
+
console.log('Field changed:', type, event.target.value);
|
|
421
|
+
}}
|
|
422
|
+
/>
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Field Configuration (`FieldConfig`)
|
|
426
|
+
|
|
427
|
+
All field types extend `BaseFieldConfig` with type-specific properties.
|
|
428
|
+
|
|
429
|
+
#### BaseFieldConfig
|
|
430
|
+
|
|
431
|
+
Common properties available on all field types:
|
|
432
|
+
|
|
433
|
+
| Prop Name | Type | Required | Default | Description |
|
|
434
|
+
|-----------|------|----------|---------|-------------|
|
|
435
|
+
| `name` | `string` | Yes | - | Field name (must match Formik field name). |
|
|
436
|
+
| `type` | `FieldType` | Yes | - | Field type. See [Field Types](#field-types). |
|
|
437
|
+
| `label` | `string` | No | - | Field label displayed above the input. |
|
|
438
|
+
| `placeholder` | `string` | No | - | Placeholder text for input fields. |
|
|
439
|
+
| `desc` | `string` | No | - | Description/helper text displayed below the field. |
|
|
440
|
+
| `info` | `string` | No | - | Info tooltip text. Shows info icon when provided. |
|
|
441
|
+
| `required` | `boolean` | No | `false` | Whether the field is required. Shows asterisk (*) indicator. |
|
|
442
|
+
| `disabled` | `boolean` | No | `false` | Whether the field is disabled. |
|
|
443
|
+
| `hidden` | `boolean` | No | `false` | Whether the field is hidden (not rendered). |
|
|
444
|
+
| `readonly` | `boolean` | No | `false` | Whether the field is read-only. |
|
|
445
|
+
| `customClass` | `string` | No | - | Custom CSS class for the field wrapper. |
|
|
446
|
+
| `customLabelClass` | `string` | No | - | Custom CSS class for the label. |
|
|
447
|
+
| `customComponentClass` | `string` | No | - | Custom CSS class for the input component. |
|
|
448
|
+
| `customDescClass` | `string` | No | - | Custom CSS class for the description text. |
|
|
449
|
+
| `customAttrClass` | `string` | No | - | Custom CSS class for attribute fields. |
|
|
450
|
+
| `dataTestId` | `string` | No | - | Data test ID for testing. Auto-generated if not provided. |
|
|
451
|
+
| `noIndent` | `boolean` | No | `false` | Remove indentation for nested/child fields. |
|
|
452
|
+
| `showOptional` | `boolean` | No | `false` | Show "(optional)" text for non-required fields. |
|
|
453
|
+
| `isButtonVisible` | `boolean` | No | `false` | Show action button (field-specific). |
|
|
454
|
+
| `showCustomError` | `boolean` | No | `false` | Always show error message even if field is not touched. |
|
|
455
|
+
| `shouldHide` | `(params: { formik, name, index? }) => boolean` | No | - | Function to conditionally hide field based on form state. |
|
|
456
|
+
| `child` | `FieldConfig[]` | No | - | Child fields shown conditionally based on parent value. |
|
|
457
|
+
| `hiddenlabel` | `string` | No | - | Value that triggers child field visibility (for dropdown/select). |
|
|
458
|
+
| `targetType` | `string` | No | - | Value that triggers child field visibility (for radio buttons). |
|
|
459
|
+
| `component` | `ReactNode` | No | - | Custom React component rendered after the field. |
|
|
460
|
+
| `blockComponent` | `ReactNode` | No | - | Custom React component rendered as a block element. |
|
|
461
|
+
| `customHandleChange` | `(event, type, index?) => void` | No | - | Custom change handler for this specific field. |
|
|
462
|
+
| `customFormChange` | `(event, type) => void` | No | - | Custom form-level change handler. |
|
|
463
|
+
| `onBlur` | `(e: FocusEvent) => void` | No | - | Custom blur handler. |
|
|
464
|
+
| `onClickLink` | `() => void` | No | - | Click handler for link fields. |
|
|
465
|
+
| `isVisibleEnable` | `boolean` | No | `false` | Show password visibility toggle (for password fields). |
|
|
466
|
+
| `isCopyEnable` | `boolean` | No | `false` | Show copy button (for text fields). |
|
|
467
|
+
| `customIcon` | `ReactNode` | No | - | Custom icon component. |
|
|
468
|
+
| `ref` | `Ref<HTMLElement>` | No | - | React ref forwarded to the input element. |
|
|
469
|
+
|
|
470
|
+
#### Field Type-Specific Configurations
|
|
471
|
+
|
|
472
|
+
##### InputField (`type: 'inputfield'`)
|
|
473
|
+
|
|
474
|
+
| Prop Name | Type | Required | Default | Description |
|
|
475
|
+
|-----------|------|----------|---------|-------------|
|
|
476
|
+
| `fieldType` | `'text' \| 'email' \| 'password' \| 'number' \| 'tel' \| 'url'` | No | `'text'` | HTML input type. |
|
|
477
|
+
| `autocomplete` | `string` | No | - | Autocomplete attribute value. |
|
|
478
|
+
| `min` | `number` | No | - | Minimum value (for number type). |
|
|
479
|
+
| `max` | `number` | No | - | Maximum value (for number type). |
|
|
480
|
+
| `suffix` | `string` | No | - | Suffix text displayed after input. |
|
|
481
|
+
| `ignoreChar` | `boolean` | No | `false` | Ignore special characters in input. |
|
|
482
|
+
|
|
483
|
+
##### TextareaField (`type: 'textarea'`)
|
|
484
|
+
|
|
485
|
+
| Prop Name | Type | Required | Default | Description |
|
|
486
|
+
|-----------|------|----------|---------|-------------|
|
|
487
|
+
| `rows` | `number` | No | `4` | Number of visible text rows. |
|
|
488
|
+
|
|
489
|
+
##### SelectField (`type: 'dropdown' \| 'singleSelect' \| 'multiSelect' \| 'asyncSelect'`)
|
|
490
|
+
|
|
491
|
+
| Prop Name | Type | Required | Default | Description |
|
|
492
|
+
|-----------|------|----------|---------|-------------|
|
|
493
|
+
| `values` | `Array<{value, label, desc?}> \| Record<string, string>` | No | `{}` | Options for select. Array format for react-select, object for native select. |
|
|
494
|
+
| `isSearchable` | `boolean` | No | `false` | Enable search/filter (react-select only). |
|
|
495
|
+
| `isMulti` | `boolean` | No | `false` | Allow multiple selections (react-select only). |
|
|
496
|
+
| `loaderCall` | `(inputValue: string) => Promise<{apps, error?}>` | No | - | Async function to load options (asyncSelect only). |
|
|
497
|
+
| `headerKey` | `string` | No | - | Header key for grouped options. |
|
|
498
|
+
| `optionsLabel` | `boolean` | No | `false` | Show label in options. |
|
|
499
|
+
| `disabledDropdownOption` | `boolean` | No | `false` | Disable dropdown option selection. |
|
|
500
|
+
| `disabledOptionText` | `string` | No | - | Text shown for disabled options. |
|
|
501
|
+
| `searchField` | `string` | No | - | Field name to search in async options. |
|
|
502
|
+
| `resetChild` | `boolean` | No | `false` | Reset child fields when selection changes. |
|
|
503
|
+
| `ignoreChar` | `boolean` | No | `false` | Ignore special characters (dropdown only). |
|
|
504
|
+
|
|
505
|
+
##### CheckboxField (`type: 'checkbox'`)
|
|
506
|
+
|
|
507
|
+
| Prop Name | Type | Required | Default | Description |
|
|
508
|
+
|-----------|------|----------|---------|-------------|
|
|
509
|
+
| `values` | `Record<string, string>` | No | `{}` | Checkbox options (for multiple checkboxes). |
|
|
510
|
+
|
|
511
|
+
##### RadioField (`type: 'radiobtn'`)
|
|
512
|
+
|
|
513
|
+
| Prop Name | Type | Required | Default | Description |
|
|
514
|
+
|-----------|------|----------|---------|-------------|
|
|
515
|
+
| `values` | `Record<string, string>` | No | `{}` | Radio button options. Key is value, value is label. |
|
|
516
|
+
|
|
517
|
+
##### DatePickerField (`type: 'datePicker' \| 'dateTimePicker'`)
|
|
518
|
+
|
|
519
|
+
| Prop Name | Type | Required | Default | Description |
|
|
520
|
+
|-----------|------|----------|---------|-------------|
|
|
521
|
+
| `dateFormat` | `string` | No | - | Date format string. |
|
|
522
|
+
| `disablePast` | `boolean` | No | `false` | Disable past dates. |
|
|
523
|
+
| `views` | `string[]` | No | - | Date picker views (MUI only). |
|
|
524
|
+
|
|
525
|
+
##### FieldArrayField (`type: 'fieldArray'`)
|
|
526
|
+
|
|
527
|
+
| Prop Name | Type | Required | Default | Description |
|
|
528
|
+
|-----------|------|----------|---------|-------------|
|
|
529
|
+
| `properties` | `{isKeyValue?: boolean, keyPlaceholder?: string, valuePlaceholder?: string}` | No | - | Configuration for key-value pairs or simple arrays. |
|
|
530
|
+
| `buttonLabel` | `string` | No | - | Label for add/remove buttons. |
|
|
531
|
+
| `addNewFieldBtnLabel` | `string` | No | `'Add New'` | Label for add button. |
|
|
532
|
+
| `minimumValuePresent` | `boolean` | No | `false` | Require at least one item. |
|
|
533
|
+
| `showAddNewFieldBtn` | `boolean` | No | `true` | Show add button. |
|
|
534
|
+
|
|
535
|
+
##### AttributeField (`type: 'attribute'`)
|
|
536
|
+
|
|
537
|
+
| Prop Name | Type | Required | Default | Description |
|
|
538
|
+
|-----------|------|----------|---------|-------------|
|
|
539
|
+
| `properties` | `FieldConfig[]` | No | `[]` | Fields within each attribute item. |
|
|
540
|
+
| `addNewField` | `Record<string, unknown>` | No | `{}` | Default values for new attribute items. |
|
|
541
|
+
| `showIllustration` | `boolean` | No | `false` | Show visual illustration. |
|
|
542
|
+
| `showAddNewFieldBtn` | `boolean` | No | `true` | Show add button. |
|
|
543
|
+
| `addNewFieldBtnLabel` | `string` | No | `'Add New'` | Label for add button. |
|
|
544
|
+
| `minimumValuePresent` | `boolean` | No | `false` | Require at least one item. |
|
|
545
|
+
|
|
546
|
+
##### CounterField (`type: 'counter'`)
|
|
547
|
+
|
|
548
|
+
| Prop Name | Type | Required | Default | Description |
|
|
549
|
+
|-----------|------|----------|---------|-------------|
|
|
550
|
+
| `min` | `number` | No | - | Minimum value. |
|
|
551
|
+
| `max` | `number` | No | - | Maximum value. |
|
|
552
|
+
| `suffix` | `string` | No | - | Suffix text displayed after counter. |
|
|
553
|
+
|
|
554
|
+
##### EditableDivField (`type: 'editablediv'`)
|
|
555
|
+
|
|
556
|
+
| Prop Name | Type | Required | Default | Description |
|
|
557
|
+
|-----------|------|----------|---------|-------------|
|
|
558
|
+
| `availableFields` | `Array<{key: string, label: string}>` | No | `[]` | Fields available for insertion into editable div. |
|
|
559
|
+
|
|
560
|
+
## Field Types
|
|
561
|
+
|
|
562
|
+
The library supports 22 field types:
|
|
563
|
+
|
|
564
|
+
1. **`inputfield`** - Text inputs (text, email, password, number, tel, url)
|
|
565
|
+
2. **`textarea`** - Multi-line text input
|
|
566
|
+
3. **`dropdown`** - Native HTML select dropdown
|
|
567
|
+
4. **`singleSelect`** - Single-select dropdown (react-select compatible)
|
|
568
|
+
5. **`multiSelect`** - Multi-select dropdown (react-select compatible)
|
|
569
|
+
6. **`asyncSelect`** - Async-loading select (react-select compatible)
|
|
570
|
+
7. **`checkbox`** - Checkbox input
|
|
571
|
+
8. **`radiobtn`** - Radio button group
|
|
572
|
+
9. **`toggle`** - Switch/toggle component
|
|
573
|
+
10. **`fileupload`** - File upload input
|
|
574
|
+
11. **`datePicker`** - Date picker
|
|
575
|
+
12. **`dateTimePicker`** - Date-time picker
|
|
576
|
+
13. **`counter`** - Counter with increment/decrement
|
|
577
|
+
14. **`fieldArray`** - Dynamic array fields (key-value or simple)
|
|
578
|
+
15. **`attribute`** - Complex nested attribute mapping
|
|
579
|
+
16. **`editablediv`** - ContentEditable with field insertion
|
|
580
|
+
17. **`text`** - Display text (read-only)
|
|
581
|
+
18. **`link`** - Clickable link field
|
|
582
|
+
19. **`component`** - Custom component wrapper
|
|
583
|
+
20. **`custom`** - Custom field with label/description
|
|
584
|
+
21. **`emptyField`** - Hidden input field
|
|
585
|
+
22. **`delete`** - Delete button/icon
|
|
586
|
+
|
|
587
|
+
## Advanced Usage
|
|
588
|
+
|
|
589
|
+
### Conditional Fields
|
|
590
|
+
|
|
591
|
+
Show/hide fields based on form values:
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
const fields = [
|
|
595
|
+
{
|
|
596
|
+
type: 'dropdown',
|
|
597
|
+
name: 'userType',
|
|
598
|
+
label: 'User Type',
|
|
599
|
+
values: {
|
|
600
|
+
'': 'Select',
|
|
601
|
+
admin: 'Admin',
|
|
602
|
+
user: 'User',
|
|
603
|
+
},
|
|
604
|
+
child: [
|
|
605
|
+
{
|
|
606
|
+
type: 'inputfield',
|
|
607
|
+
name: 'adminCode',
|
|
608
|
+
label: 'Admin Code',
|
|
609
|
+
hiddenlabel: 'admin', // Shown when userType === 'admin'
|
|
610
|
+
},
|
|
611
|
+
],
|
|
612
|
+
},
|
|
613
|
+
];
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
### Dynamic Validation
|
|
617
|
+
|
|
618
|
+
Use Yup's dynamic validation:
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
const validationSchema = Yup.object({
|
|
622
|
+
email: Yup.string().email().required(),
|
|
623
|
+
password: Yup.string()
|
|
624
|
+
.min(8)
|
|
625
|
+
.when('confirmPassword', {
|
|
626
|
+
is: (value: string) => value,
|
|
627
|
+
then: (schema) => schema.required('Password is required'),
|
|
628
|
+
}),
|
|
629
|
+
confirmPassword: Yup.string()
|
|
630
|
+
.oneOf([Yup.ref('password')], 'Passwords must match')
|
|
631
|
+
.required(),
|
|
632
|
+
});
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Custom Field Renderers
|
|
636
|
+
|
|
637
|
+
Inject custom React components:
|
|
638
|
+
|
|
639
|
+
```typescript
|
|
640
|
+
const fields = [
|
|
641
|
+
{
|
|
642
|
+
type: 'inputfield',
|
|
643
|
+
name: 'email',
|
|
644
|
+
label: 'Email',
|
|
645
|
+
component: <CustomEmailValidator />, // Rendered after field
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
type: 'custom',
|
|
649
|
+
name: 'customSection',
|
|
650
|
+
label: 'Custom Section',
|
|
651
|
+
blockComponent: <MyCustomComponent />, // Rendered as block
|
|
652
|
+
},
|
|
653
|
+
];
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
### Field Arrays
|
|
657
|
+
|
|
658
|
+
Dynamic key-value pairs:
|
|
659
|
+
|
|
660
|
+
```typescript
|
|
661
|
+
const fields = [
|
|
662
|
+
{
|
|
663
|
+
type: 'fieldArray',
|
|
664
|
+
name: 'metadata',
|
|
665
|
+
label: 'Metadata',
|
|
666
|
+
properties: {
|
|
667
|
+
isKeyValue: true,
|
|
668
|
+
keyPlaceholder: 'Key',
|
|
669
|
+
valuePlaceholder: 'Value',
|
|
670
|
+
},
|
|
671
|
+
addNewFieldBtnLabel: 'Add Metadata',
|
|
672
|
+
},
|
|
673
|
+
];
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### Async Select
|
|
677
|
+
|
|
678
|
+
Load options from API:
|
|
679
|
+
|
|
680
|
+
```typescript
|
|
681
|
+
const fields = [
|
|
682
|
+
{
|
|
683
|
+
type: 'asyncSelect',
|
|
684
|
+
name: 'user',
|
|
685
|
+
label: 'Select User',
|
|
686
|
+
loaderCall: async (inputValue: string) => {
|
|
687
|
+
const response = await fetch(`/api/users?search=${inputValue}`);
|
|
688
|
+
const data = await response.json();
|
|
689
|
+
return {
|
|
690
|
+
apps: data.users.map((user: any) => ({
|
|
691
|
+
value: user.id,
|
|
692
|
+
label: user.name,
|
|
693
|
+
})),
|
|
694
|
+
};
|
|
695
|
+
},
|
|
696
|
+
},
|
|
697
|
+
];
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
### Custom Adapters
|
|
701
|
+
|
|
702
|
+
Create adapters for any UI library. See [ADAPTER_GUIDE.md](./ADAPTER_GUIDE.md) for detailed instructions.
|
|
703
|
+
|
|
704
|
+
```typescript
|
|
705
|
+
import { UILibraryAdapter } from 'dynamic-formik-form';
|
|
706
|
+
|
|
707
|
+
export const createMyLibraryAdapter = (
|
|
708
|
+
Input,
|
|
709
|
+
Button,
|
|
710
|
+
// ... your UI components
|
|
711
|
+
): UILibraryAdapter => {
|
|
712
|
+
return {
|
|
713
|
+
Input: ({ error, ...props }) => (
|
|
714
|
+
<Input {...props} hasError={error} />
|
|
715
|
+
),
|
|
716
|
+
// ... map other components
|
|
717
|
+
};
|
|
718
|
+
};
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
## Architecture
|
|
722
|
+
|
|
723
|
+
### Core vs Adapter Responsibility
|
|
724
|
+
|
|
725
|
+
- **Core Library**: Handles form logic, validation, field rendering, conditional logic, and state management
|
|
726
|
+
- **Adapters**: Map UI library components to a common interface. No form logic.
|
|
727
|
+
|
|
728
|
+
### Why Adapter Pattern?
|
|
729
|
+
|
|
730
|
+
1. **Flexibility**: Users choose their UI library without vendor lock-in
|
|
731
|
+
2. **Tree-Shaking**: Only bundle the adapter you use
|
|
732
|
+
3. **Version Control**: Users control UI library versions (peer dependencies)
|
|
733
|
+
4. **Extensibility**: Easy to add new UI libraries without modifying core
|
|
734
|
+
5. **Testing**: Adapters can be mocked/swapped easily
|
|
735
|
+
|
|
736
|
+
### Adding New UI Library Support
|
|
737
|
+
|
|
738
|
+
1. Create adapter factory function (see `src/adapters/`)
|
|
739
|
+
2. Create icon factory function (see `src/icons/`)
|
|
740
|
+
3. Export from `src/adapters/index.ts` and `src/icons/index.ts`
|
|
741
|
+
4. Document usage in README
|
|
742
|
+
|
|
743
|
+
See [ADAPTER_GUIDE.md](./ADAPTER_GUIDE.md) for complete guide.
|
|
744
|
+
|
|
745
|
+
## Limitations & Roadmap
|
|
746
|
+
|
|
747
|
+
### Current Limitations
|
|
748
|
+
|
|
749
|
+
- Date pickers require UI library adapters (MUI DatePicker, Ant Design DatePicker, etc.)
|
|
750
|
+
- File uploads are basic (no drag-and-drop, preview, or progress)
|
|
751
|
+
- Async select requires react-select (not included as dependency)
|
|
752
|
+
- Some advanced field types (editablediv, attribute) are complex and may need customization
|
|
753
|
+
|
|
754
|
+
### Roadmap
|
|
755
|
+
|
|
756
|
+
- [ ] Built-in drag-and-drop file upload
|
|
757
|
+
- [ ] More date picker adapters
|
|
758
|
+
- [ ] Form builder UI component
|
|
759
|
+
- [ ] Form schema validator
|
|
760
|
+
- [ ] Performance optimizations for large forms
|
|
761
|
+
- [ ] Accessibility improvements
|
|
762
|
+
- [ ] More examples and recipes
|
|
763
|
+
|
|
764
|
+
## Contributing
|
|
765
|
+
|
|
766
|
+
Contributions are welcome! Please:
|
|
767
|
+
|
|
768
|
+
1. Fork the repository
|
|
769
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
770
|
+
3. Make your changes
|
|
771
|
+
4. Add tests if applicable
|
|
772
|
+
5. Ensure all tests pass (`npm test`)
|
|
773
|
+
6. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
774
|
+
7. Push to the branch (`git push origin feature/amazing-feature`)
|
|
775
|
+
8. Open a Pull Request
|
|
776
|
+
|
|
777
|
+
### Development Setup
|
|
778
|
+
|
|
779
|
+
```bash
|
|
780
|
+
# Clone repository
|
|
781
|
+
git clone <repository-url>
|
|
782
|
+
cd dynamic-form
|
|
783
|
+
|
|
784
|
+
# Install dependencies
|
|
785
|
+
npm install
|
|
786
|
+
|
|
787
|
+
# Run tests
|
|
788
|
+
npm test
|
|
789
|
+
|
|
790
|
+
# Build
|
|
791
|
+
npm run build
|
|
792
|
+
|
|
793
|
+
# Development mode
|
|
794
|
+
npm run dev
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
## License
|
|
798
|
+
|
|
799
|
+
MIT Β© Ambar Bidkar
|
|
800
|
+
|
|
801
|
+
## Support
|
|
802
|
+
|
|
803
|
+
- π [Documentation](./README.md)
|
|
804
|
+
- π [Report Bug](https://github.com/yourusername/dynamic-form/issues)
|
|
805
|
+
- π‘ [Request Feature](https://github.com/yourusername/dynamic-form/issues)
|
|
806
|
+
- π§ [Email Support](mailto:support@example.com)
|
|
807
|
+
|
|
808
|
+
---
|
|
809
|
+
|
|
810
|
+
**Made with β€οΈ for the React community**
|