extended-dynamic-forms 0.1.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 +583 -0
- package/dist/ExtendedForm.d.ts +4 -0
- package/dist/components/fields/AddressField.d.ts +4 -0
- package/dist/components/fields/CurrencyField.d.ts +4 -0
- package/dist/components/fields/PhoneField.d.ts +4 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/ui/badge.d.ts +9 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/card.d.ts +8 -0
- package/dist/components/ui/input.d.ts +5 -0
- package/dist/components/ui/label.d.ts +5 -0
- package/dist/components/ui/slider.d.ts +4 -0
- package/dist/events/FormEventContext.d.ts +20 -0
- package/dist/events/FormEventHub.d.ts +27 -0
- package/dist/events/FormEventManager.d.ts +10 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.es.js +4748 -0
- package/dist/index.umd.js +50 -0
- package/dist/layouts/grid/GridLayout.d.ts +11 -0
- package/dist/layouts/horizontal/HorizontalLayout.d.ts +10 -0
- package/dist/layouts/index.d.ts +3 -0
- package/dist/layouts/vertical/VerticalLayout.d.ts +9 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/style.css +1 -0
- package/dist/types/index.d.ts +47 -0
- package/dist/utils/formDataUtils.d.ts +22 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/schemaUtils.d.ts +24 -0
- package/dist/utils/validationUtils.d.ts +26 -0
- package/dist/widgets/date/DateWidget.d.ts +4 -0
- package/dist/widgets/file/FileWidget.d.ts +4 -0
- package/dist/widgets/index.d.ts +14 -0
- package/dist/widgets/input/ColorWidget.d.ts +4 -0
- package/dist/widgets/input/RangeWidget.d.ts +4 -0
- package/dist/widgets/input/RatingWidget.d.ts +4 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
# Extended Dynamic Forms
|
|
2
|
+
|
|
3
|
+
A comprehensive extension of React JSON Schema Form (RJSF) v5 with shadcn/ui components, custom widgets, fields, layouts, and a powerful central event orchestration system with webhook capabilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **shadcn/ui Integration**: Built on top of @rjsf/shadcn for beautiful, accessible components
|
|
8
|
+
- ✨ **Custom Widgets**: Color picker, date picker, file upload, range slider, rating stars
|
|
9
|
+
- 🏗️ **Custom Fields**: Address, phone number, currency input with formatting
|
|
10
|
+
- 📐 **Layout Components**: Vertical, horizontal, and grid layouts
|
|
11
|
+
- 🎯 **Central Event Orchestration**: Unified system capturing all form field events (focus, blur, change)
|
|
12
|
+
- 🔗 **Webhook Integration**: Configure multiple webhook endpoints with debouncing and retry logic
|
|
13
|
+
- 🔧 **Enhanced Form Events**: Before/after submit and validation hooks plus field-level events
|
|
14
|
+
- 📦 **TypeScript Support**: Fully typed with comprehensive type definitions
|
|
15
|
+
- ⚡ **Vite Powered**: Fast development and optimized builds
|
|
16
|
+
- 🎯 **Tailwind CSS**: Utility-first styling with full customization
|
|
17
|
+
- 🛡️ **Error Handling**: Robust error recovery and retry mechanisms
|
|
18
|
+
- ⚡ **Performance Optimized**: Debouncing, queuing, and async processing
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install extended-dynamic-forms
|
|
24
|
+
# or
|
|
25
|
+
yarn add extended-dynamic-forms
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Peer Dependencies
|
|
29
|
+
|
|
30
|
+
This package requires the following peer dependencies:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"react": "^18.0.0",
|
|
35
|
+
"react-dom": "^18.0.0",
|
|
36
|
+
"@rjsf/core": "^5.15.0",
|
|
37
|
+
"@rjsf/utils": "^5.15.0",
|
|
38
|
+
"@rjsf/validator-ajv8": "^5.15.0",
|
|
39
|
+
"@rjsf/shadcn": "^0.1.0"
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Setup
|
|
44
|
+
|
|
45
|
+
### CSS Requirements
|
|
46
|
+
|
|
47
|
+
This package uses Tailwind CSS and shadcn/ui components. Make sure to include the required CSS in your application:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// In your main entry file (e.g., App.tsx or index.tsx)
|
|
51
|
+
import 'extended-dynamic-forms/dist/index.css';
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If you're using Tailwind CSS in your project, ensure your `tailwind.config.js` includes the package in the content array:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
module.exports = {
|
|
58
|
+
content: [
|
|
59
|
+
// ... your other content paths
|
|
60
|
+
"./node_modules/extended-dynamic-forms/**/*.{js,ts,jsx,tsx}",
|
|
61
|
+
],
|
|
62
|
+
// ... rest of your config
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Basic Form
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import React from 'react';
|
|
72
|
+
import { ExtendedForm } from 'extended-dynamic-forms';
|
|
73
|
+
|
|
74
|
+
const schema = {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
name: { type: 'string', title: 'Name' },
|
|
78
|
+
email: { type: 'string', format: 'email', title: 'Email' },
|
|
79
|
+
rating: { type: 'number', title: 'Rating' },
|
|
80
|
+
favoriteColor: { type: 'string', title: 'Favorite Color' }
|
|
81
|
+
},
|
|
82
|
+
required: ['name', 'email']
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const uiSchema = {
|
|
86
|
+
rating: {
|
|
87
|
+
'ui:widget': 'rating',
|
|
88
|
+
'ui:options': { max: 5 }
|
|
89
|
+
},
|
|
90
|
+
favoriteColor: {
|
|
91
|
+
'ui:widget': 'color'
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function App() {
|
|
96
|
+
const handleSubmit = ({ formData }) => {
|
|
97
|
+
console.log('Form data:', formData);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleFieldChange = (event) => {
|
|
101
|
+
console.log(`Field ${event.fieldId} changed to:`, event.fieldValue);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<ExtendedForm
|
|
106
|
+
schema={schema}
|
|
107
|
+
uiSchema={uiSchema}
|
|
108
|
+
onSubmit={handleSubmit}
|
|
109
|
+
onFieldChange={handleFieldChange}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### With Webhook Integration
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import React from 'react';
|
|
119
|
+
import { ExtendedForm, WebhookConfig } from 'extended-dynamic-forms';
|
|
120
|
+
|
|
121
|
+
const webhooks: WebhookConfig[] = [
|
|
122
|
+
{
|
|
123
|
+
url: 'https://your-api.com/webhooks/form-events',
|
|
124
|
+
events: ['change'],
|
|
125
|
+
debounceMs: 300,
|
|
126
|
+
headers: {
|
|
127
|
+
'Authorization': 'Bearer your-token'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
function App() {
|
|
133
|
+
return (
|
|
134
|
+
<ExtendedForm
|
|
135
|
+
schema={schema}
|
|
136
|
+
uiSchema={uiSchema}
|
|
137
|
+
webhooks={webhooks}
|
|
138
|
+
onSubmit={({ formData }) => {
|
|
139
|
+
console.log('Form submitted:', formData);
|
|
140
|
+
}}
|
|
141
|
+
onFieldFocus={(event) => {
|
|
142
|
+
console.log('Field focused:', event.fieldId);
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Custom Widgets
|
|
150
|
+
|
|
151
|
+
### Color Widget
|
|
152
|
+
```tsx
|
|
153
|
+
const uiSchema = {
|
|
154
|
+
favoriteColor: {
|
|
155
|
+
'ui:widget': 'color'
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Date Widget
|
|
161
|
+
```tsx
|
|
162
|
+
const uiSchema = {
|
|
163
|
+
birthDate: {
|
|
164
|
+
'ui:widget': 'date',
|
|
165
|
+
'ui:options': {
|
|
166
|
+
inputType: 'datetime-local', // or 'date', 'time'
|
|
167
|
+
minDate: '2000-01-01',
|
|
168
|
+
maxDate: '2025-12-31'
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### File Widget
|
|
175
|
+
```tsx
|
|
176
|
+
const uiSchema = {
|
|
177
|
+
documents: {
|
|
178
|
+
'ui:widget': 'file',
|
|
179
|
+
'ui:options': {
|
|
180
|
+
accept: '.pdf,.doc,.docx',
|
|
181
|
+
multiple: true
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Range Widget
|
|
188
|
+
```tsx
|
|
189
|
+
const schema = {
|
|
190
|
+
volume: {
|
|
191
|
+
type: 'number',
|
|
192
|
+
minimum: 0,
|
|
193
|
+
maximum: 100,
|
|
194
|
+
multipleOf: 5
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const uiSchema = {
|
|
199
|
+
volume: {
|
|
200
|
+
'ui:widget': 'range'
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Rating Widget
|
|
206
|
+
```tsx
|
|
207
|
+
const uiSchema = {
|
|
208
|
+
satisfaction: {
|
|
209
|
+
'ui:widget': 'rating',
|
|
210
|
+
'ui:options': {
|
|
211
|
+
max: 10 // default is 5
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Custom Fields
|
|
218
|
+
|
|
219
|
+
### Address Field
|
|
220
|
+
```tsx
|
|
221
|
+
const schema = {
|
|
222
|
+
homeAddress: {
|
|
223
|
+
type: 'object',
|
|
224
|
+
properties: {
|
|
225
|
+
street: { type: 'string' },
|
|
226
|
+
city: { type: 'string' },
|
|
227
|
+
state: { type: 'string' },
|
|
228
|
+
zip: { type: 'string' },
|
|
229
|
+
country: { type: 'string' }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const uiSchema = {
|
|
235
|
+
homeAddress: {
|
|
236
|
+
'ui:field': 'address'
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Phone Field
|
|
242
|
+
```tsx
|
|
243
|
+
const uiSchema = {
|
|
244
|
+
phoneNumber: {
|
|
245
|
+
'ui:field': 'phone'
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Currency Field
|
|
251
|
+
```tsx
|
|
252
|
+
const uiSchema = {
|
|
253
|
+
price: {
|
|
254
|
+
'ui:field': 'currency',
|
|
255
|
+
'ui:options': {
|
|
256
|
+
currency: 'EUR',
|
|
257
|
+
symbol: '€'
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Central Event Orchestration System
|
|
264
|
+
|
|
265
|
+
The library features a comprehensive event orchestration system that captures all form field events and enables webhook integration.
|
|
266
|
+
|
|
267
|
+
### Field-Level Event Handling
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import { ExtendedForm, FormFieldEvent } from 'extended-dynamic-forms';
|
|
271
|
+
|
|
272
|
+
<ExtendedForm
|
|
273
|
+
schema={schema}
|
|
274
|
+
uiSchema={uiSchema}
|
|
275
|
+
formData={formData}
|
|
276
|
+
|
|
277
|
+
// Field-level event handlers
|
|
278
|
+
onFieldFocus={(event: FormFieldEvent) => {
|
|
279
|
+
console.log(`Field ${event.fieldId} focused:`, event.fieldValue);
|
|
280
|
+
// Track user interactions for analytics
|
|
281
|
+
}}
|
|
282
|
+
|
|
283
|
+
onFieldChange={(event: FormFieldEvent) => {
|
|
284
|
+
console.log(`Field ${event.fieldId} changed:`, event.fieldValue);
|
|
285
|
+
// Real-time validation or processing
|
|
286
|
+
if (event.fieldId === 'root_email') {
|
|
287
|
+
validateEmailRealTime(event.fieldValue);
|
|
288
|
+
}
|
|
289
|
+
}}
|
|
290
|
+
|
|
291
|
+
onFieldBlur={(event: FormFieldEvent) => {
|
|
292
|
+
console.log(`Field ${event.fieldId} blurred:`, event.fieldValue);
|
|
293
|
+
// Format field values or trigger validation
|
|
294
|
+
}}
|
|
295
|
+
|
|
296
|
+
// Traditional form-level events (still supported)
|
|
297
|
+
beforeSubmit={async (data) => {
|
|
298
|
+
return { ...data, timestamp: Date.now() };
|
|
299
|
+
}}
|
|
300
|
+
|
|
301
|
+
afterSubmit={async (data) => {
|
|
302
|
+
console.log('Form submitted:', data);
|
|
303
|
+
}}
|
|
304
|
+
/>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Webhook Integration
|
|
308
|
+
|
|
309
|
+
Configure multiple webhook endpoints to receive real-time form events:
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
import { ExtendedForm, WebhookConfig } from 'extended-dynamic-forms';
|
|
313
|
+
|
|
314
|
+
const webhookConfigs: WebhookConfig[] = [
|
|
315
|
+
{
|
|
316
|
+
url: 'https://api.yourapp.com/form-events',
|
|
317
|
+
method: 'POST',
|
|
318
|
+
events: ['change', 'blur'], // Only trigger on change and blur
|
|
319
|
+
debounceMs: 500, // Wait 500ms before sending
|
|
320
|
+
retries: 3, // Retry failed requests
|
|
321
|
+
timeout: 5000, // 5 second timeout
|
|
322
|
+
headers: {
|
|
323
|
+
'Authorization': 'Bearer your-api-token',
|
|
324
|
+
'X-Form-ID': 'contact-form',
|
|
325
|
+
'Content-Type': 'application/json'
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
url: 'https://analytics.yourapp.com/field-interactions',
|
|
330
|
+
events: ['focus'], // Track focus events for analytics
|
|
331
|
+
debounceMs: 0, // No debouncing for real-time analytics
|
|
332
|
+
retries: 1,
|
|
333
|
+
headers: {
|
|
334
|
+
'X-Analytics-Key': 'your-analytics-key'
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
<ExtendedForm
|
|
340
|
+
schema={schema}
|
|
341
|
+
uiSchema={uiSchema}
|
|
342
|
+
webhooks={webhookConfigs}
|
|
343
|
+
|
|
344
|
+
onFieldChange={(event) => {
|
|
345
|
+
// Local processing alongside webhooks
|
|
346
|
+
if (event.fieldId === 'root_email') {
|
|
347
|
+
setEmailValid(validateEmail(event.fieldValue));
|
|
348
|
+
}
|
|
349
|
+
}}
|
|
350
|
+
/>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Webhook Payload Structure
|
|
354
|
+
|
|
355
|
+
Your webhook endpoints will receive payloads in this format:
|
|
356
|
+
|
|
357
|
+
```json
|
|
358
|
+
{
|
|
359
|
+
"event": {
|
|
360
|
+
"eventType": "change",
|
|
361
|
+
"fieldId": "root_email",
|
|
362
|
+
"fieldValue": "user@example.com",
|
|
363
|
+
"schema": {
|
|
364
|
+
"type": "string",
|
|
365
|
+
"format": "email",
|
|
366
|
+
"title": "Email Address"
|
|
367
|
+
},
|
|
368
|
+
"formData": {
|
|
369
|
+
"email": "user@example.com",
|
|
370
|
+
"name": "John Doe"
|
|
371
|
+
},
|
|
372
|
+
"timestamp": 1678901234567,
|
|
373
|
+
"fieldPath": ["email"]
|
|
374
|
+
},
|
|
375
|
+
"timestamp": 1678901234567,
|
|
376
|
+
"source": "extended-dynamic-forms"
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Advanced Features
|
|
381
|
+
|
|
382
|
+
- **Debouncing**: Prevent webhook spam on rapid input changes
|
|
383
|
+
- **Retry Logic**: Automatic retry with exponential backoff for failed requests
|
|
384
|
+
- **Error Isolation**: Webhook failures don't block form functionality
|
|
385
|
+
- **Event Filtering**: Configure which events trigger each webhook
|
|
386
|
+
- **Queue Management**: Asynchronous processing with proper ordering
|
|
387
|
+
- **Custom Headers**: Add authentication, tracking, or custom metadata
|
|
388
|
+
|
|
389
|
+
## Layouts
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
import { VerticalLayout, HorizontalLayout, GridLayout } from 'extended-dynamic-forms';
|
|
393
|
+
|
|
394
|
+
// Vertical Layout
|
|
395
|
+
<VerticalLayout spacing="medium">
|
|
396
|
+
<input />
|
|
397
|
+
<button />
|
|
398
|
+
</VerticalLayout>
|
|
399
|
+
|
|
400
|
+
// Horizontal Layout
|
|
401
|
+
<HorizontalLayout spacing="large" align="center">
|
|
402
|
+
<label />
|
|
403
|
+
<input />
|
|
404
|
+
</HorizontalLayout>
|
|
405
|
+
|
|
406
|
+
// Grid Layout
|
|
407
|
+
<GridLayout columns={3} gap="medium">
|
|
408
|
+
<Card />
|
|
409
|
+
<Card />
|
|
410
|
+
<Card />
|
|
411
|
+
</GridLayout>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Use Cases
|
|
415
|
+
|
|
416
|
+
### Real-time Form Analytics
|
|
417
|
+
```tsx
|
|
418
|
+
const webhooks: WebhookConfig[] = [
|
|
419
|
+
{
|
|
420
|
+
url: 'https://analytics.yourapp.com/form-interactions',
|
|
421
|
+
events: ['focus', 'blur', 'change'],
|
|
422
|
+
debounceMs: 100,
|
|
423
|
+
headers: { 'X-Analytics-Token': 'your-token' }
|
|
424
|
+
}
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
// Track user behavior, field completion rates, abandonment points
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Auto-save & Recovery
|
|
431
|
+
```tsx
|
|
432
|
+
<ExtendedForm
|
|
433
|
+
schema={schema}
|
|
434
|
+
onFieldChange={(event) => {
|
|
435
|
+
// Auto-save to localStorage or server
|
|
436
|
+
localStorage.setItem(`form_${event.fieldId}`, event.fieldValue);
|
|
437
|
+
}}
|
|
438
|
+
webhooks={[{
|
|
439
|
+
url: 'https://api.yourapp.com/autosave',
|
|
440
|
+
events: ['change'],
|
|
441
|
+
debounceMs: 2000 // Save every 2 seconds
|
|
442
|
+
}]}
|
|
443
|
+
/>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Real-time Validation
|
|
447
|
+
```tsx
|
|
448
|
+
<ExtendedForm
|
|
449
|
+
schema={schema}
|
|
450
|
+
onFieldBlur={async (event) => {
|
|
451
|
+
if (event.fieldId === 'root_email') {
|
|
452
|
+
const isValid = await validateEmailWithAPI(event.fieldValue);
|
|
453
|
+
setEmailValidation(isValid);
|
|
454
|
+
}
|
|
455
|
+
}}
|
|
456
|
+
webhooks={[{
|
|
457
|
+
url: 'https://validation.yourapp.com/validate',
|
|
458
|
+
events: ['blur'],
|
|
459
|
+
headers: { 'Content-Type': 'application/json' }
|
|
460
|
+
}]}
|
|
461
|
+
/>
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Progressive Form Enhancement
|
|
465
|
+
```tsx
|
|
466
|
+
<ExtendedForm
|
|
467
|
+
schema={schema}
|
|
468
|
+
onFieldChange={(event) => {
|
|
469
|
+
// Show additional fields based on user input
|
|
470
|
+
if (event.fieldId === 'root_country' && event.fieldValue === 'US') {
|
|
471
|
+
setShowStateField(true);
|
|
472
|
+
}
|
|
473
|
+
}}
|
|
474
|
+
/>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## API Reference
|
|
478
|
+
|
|
479
|
+
### ExtendedFormProps
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
interface ExtendedFormProps extends FormProps {
|
|
483
|
+
// Standard RJSF props
|
|
484
|
+
schema: RJSFSchema;
|
|
485
|
+
uiSchema?: UiSchema;
|
|
486
|
+
formData?: any;
|
|
487
|
+
onSubmit?: (data: any, event: any) => void;
|
|
488
|
+
onChange?: (data: any) => void;
|
|
489
|
+
|
|
490
|
+
// Field-level event handlers
|
|
491
|
+
onFieldFocus?: (event: FormFieldEvent) => Promise<void> | void;
|
|
492
|
+
onFieldBlur?: (event: FormFieldEvent) => Promise<void> | void;
|
|
493
|
+
onFieldChange?: (event: FormFieldEvent) => Promise<void> | void;
|
|
494
|
+
|
|
495
|
+
// Webhook configuration
|
|
496
|
+
webhooks?: WebhookConfig[];
|
|
497
|
+
|
|
498
|
+
// Form lifecycle events
|
|
499
|
+
beforeSubmit?: (data: any) => Promise<any> | any;
|
|
500
|
+
afterSubmit?: (data: any) => Promise<void> | void;
|
|
501
|
+
beforeValidation?: (data: any) => Promise<any> | any;
|
|
502
|
+
afterValidation?: (data: any, errors: any) => Promise<void> | void;
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### FormFieldEvent
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
interface FormFieldEvent {
|
|
510
|
+
eventType: 'focus' | 'blur' | 'change';
|
|
511
|
+
fieldId: string;
|
|
512
|
+
fieldValue: any;
|
|
513
|
+
schema?: any;
|
|
514
|
+
formData?: any;
|
|
515
|
+
timestamp: number;
|
|
516
|
+
fieldPath?: string[];
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### WebhookConfig
|
|
521
|
+
|
|
522
|
+
```typescript
|
|
523
|
+
interface WebhookConfig {
|
|
524
|
+
url: string;
|
|
525
|
+
method?: 'POST' | 'PUT' | 'PATCH';
|
|
526
|
+
headers?: Record<string, string>;
|
|
527
|
+
events?: ('focus' | 'blur' | 'change')[];
|
|
528
|
+
debounceMs?: number;
|
|
529
|
+
retries?: number;
|
|
530
|
+
timeout?: number;
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
## Utility Functions
|
|
535
|
+
|
|
536
|
+
```tsx
|
|
537
|
+
import { formDataUtils, schemaUtils, validationUtils } from 'extended-dynamic-forms';
|
|
538
|
+
|
|
539
|
+
// Form Data Utils
|
|
540
|
+
const clonedData = formDataUtils.cloneFormData(formData);
|
|
541
|
+
const mergedData = formDataUtils.mergeFormData(current, updates);
|
|
542
|
+
const nestedValue = formDataUtils.getNestedValue(data, 'user.address.city');
|
|
543
|
+
const updatedData = formDataUtils.setNestedValue(data, 'user.name', 'John');
|
|
544
|
+
const cleanData = formDataUtils.removeEmptyValues(data);
|
|
545
|
+
|
|
546
|
+
// Schema Utils
|
|
547
|
+
const required = schemaUtils.getRequiredFields(schema);
|
|
548
|
+
const isRequired = schemaUtils.isFieldRequired(schema, 'email');
|
|
549
|
+
const fieldSchema = schemaUtils.getFieldSchema(schema, 'user.address');
|
|
550
|
+
const merged = schemaUtils.mergeSchemas(schema1, schema2);
|
|
551
|
+
const paths = schemaUtils.getAllFieldPaths(schema);
|
|
552
|
+
|
|
553
|
+
// Validation Utils
|
|
554
|
+
const isEmail = validationUtils.isValidEmail('test@example.com');
|
|
555
|
+
const isPhone = validationUtils.isValidPhone('(555) 123-4567');
|
|
556
|
+
const isUrl = validationUtils.isValidUrl('https://example.com');
|
|
557
|
+
const isDate = validationUtils.isValidDate('2024-01-01');
|
|
558
|
+
const inRange = validationUtils.isInRange(5, 1, 10);
|
|
559
|
+
const message = validationUtils.getValidationMessage('required');
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## Development
|
|
563
|
+
|
|
564
|
+
```bash
|
|
565
|
+
# Install dependencies
|
|
566
|
+
npm install
|
|
567
|
+
|
|
568
|
+
# Run development server
|
|
569
|
+
npm run dev
|
|
570
|
+
|
|
571
|
+
# Build library
|
|
572
|
+
npm run build
|
|
573
|
+
|
|
574
|
+
# Run tests
|
|
575
|
+
npm test
|
|
576
|
+
|
|
577
|
+
# Lint code
|
|
578
|
+
npm run lint
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
## License
|
|
582
|
+
|
|
583
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AddressField } from './fields/AddressField';
|
|
2
|
+
import { PhoneField } from './fields/PhoneField';
|
|
3
|
+
import { CurrencyField } from './fields/CurrencyField';
|
|
4
|
+
|
|
5
|
+
export declare const customFields: {
|
|
6
|
+
address: import('react').FC<import('@rjsf/utils').FieldProps<any, import('@rjsf/utils').RJSFSchema, any>>;
|
|
7
|
+
phone: import('react').FC<import('@rjsf/utils').FieldProps<any, import('@rjsf/utils').RJSFSchema, any>>;
|
|
8
|
+
currency: import('react').FC<import('@rjsf/utils').FieldProps<any, import('@rjsf/utils').RJSFSchema, any>>;
|
|
9
|
+
};
|
|
10
|
+
export { AddressField, PhoneField, CurrencyField, };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
declare const badgeVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | null | undefined;
|
|
5
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
6
|
+
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
7
|
+
}
|
|
8
|
+
declare function Badge({ className, variant, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
declare const buttonVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
|
+
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
6
|
+
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
7
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
11
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
3
|
+
declare const CardHeader: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
4
|
+
declare const CardTitle: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLParagraphElement>>;
|
|
5
|
+
declare const CardDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
|
|
6
|
+
declare const CardContent: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
7
|
+
declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
4
|
+
declare const Label: React.ForwardRefExoticComponent<Omit<LabelPrimitive.LabelProps & React.RefAttributes<HTMLLabelElement>, "ref"> & VariantProps<(props?: import('class-variance-authority/types').ClassProp | undefined) => string> & React.RefAttributes<HTMLLabelElement>>;
|
|
5
|
+
export { Label };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SliderPrimitive from "@radix-ui/react-slider";
|
|
3
|
+
declare const Slider: React.ForwardRefExoticComponent<Omit<SliderPrimitive.SliderProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
|
|
4
|
+
export { Slider };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
import { FormEventHub } from './FormEventHub';
|
|
3
|
+
|
|
4
|
+
interface FormEventContextType {
|
|
5
|
+
eventHub: FormEventHub;
|
|
6
|
+
formData?: any;
|
|
7
|
+
}
|
|
8
|
+
interface FormEventProviderProps {
|
|
9
|
+
eventHub: FormEventHub;
|
|
10
|
+
formData?: any;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare const FormEventProvider: React.FC<FormEventProviderProps>;
|
|
14
|
+
export declare const useFormEventHub: () => FormEventContextType;
|
|
15
|
+
export declare const useFieldEventHandlers: (fieldId: string, schema?: any, fieldPath?: string[]) => {
|
|
16
|
+
onFocus: (value: any) => Promise<void>;
|
|
17
|
+
onBlur: (value: any) => Promise<void>;
|
|
18
|
+
onChange: (value: any) => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
export {};
|