extended-dynamic-forms 0.1.15 → 0.1.18
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 +21 -0
- package/README.md +859 -551
- package/dist/extended-dynamic-forms.standalone.js +882 -0
- package/dist/style.css +1 -1
- package/examples/conditional-v2-example.tsx +64 -0
- package/examples/dynamic-array-items-example.tsx +211 -0
- package/examples/form-devtools-example.tsx +386 -0
- package/examples/global-ui-rules-example.tsx +356 -0
- package/examples/vanilla-js/README.md +212 -0
- package/examples/vanilla-js/basic-form.html +142 -0
- package/examples/vanilla-js/conditional-form.html +159 -0
- package/examples/vanilla-js/wizard-form.html +185 -0
- package/package.json +53 -37
- package/dist/ExtendedForm.d.ts +0 -4
- package/dist/components/arrays/ArrayFieldItemTemplate.d.ts +0 -3
- package/dist/components/arrays/ArrayFieldTemplate.d.ts +0 -3
- package/dist/components/arrays/index.d.ts +0 -2
- package/dist/components/fields/AddressField.d.ts +0 -4
- package/dist/components/fields/CurrencyField.d.ts +0 -4
- package/dist/components/fields/PhoneField.d.ts +0 -4
- package/dist/components/index.d.ts +0 -15
- package/dist/components/ui/badge.d.ts +0 -9
- package/dist/components/ui/button.d.ts +0 -11
- package/dist/components/ui/calendar.d.ts +0 -8
- package/dist/components/ui/card.d.ts +0 -8
- package/dist/components/ui/checkbox.d.ts +0 -4
- package/dist/components/ui/date-picker.d.ts +0 -13
- package/dist/components/ui/input.d.ts +0 -5
- package/dist/components/ui/label.d.ts +0 -5
- package/dist/components/ui/popover.d.ts +0 -6
- package/dist/components/ui/select.d.ts +0 -13
- package/dist/components/ui/slider.d.ts +0 -4
- package/dist/components/ui/textarea.d.ts +0 -5
- package/dist/conditionals/ConditionalForm.d.ts +0 -7
- package/dist/conditionals/index.d.ts +0 -4
- package/dist/conditionals/initialStateUtils.d.ts +0 -18
- package/dist/conditionals/types.d.ts +0 -65
- package/dist/conditionals/utils.d.ts +0 -22
- package/dist/events/FormEventContext.d.ts +0 -20
- package/dist/events/FormEventHub.d.ts +0 -27
- package/dist/events/FormEventManager.d.ts +0 -10
- package/dist/events/index.d.ts +0 -4
- package/dist/index.d.ts +0 -15
- package/dist/index.es.js +0 -15405
- package/dist/index.umd.js +0 -116
- package/dist/layouts/grid/GridLayout.d.ts +0 -11
- package/dist/layouts/horizontal/HorizontalLayout.d.ts +0 -10
- package/dist/layouts/index.d.ts +0 -3
- package/dist/layouts/vertical/VerticalLayout.d.ts +0 -9
- package/dist/lib/utils.d.ts +0 -3
- package/dist/types/index.d.ts +0 -47
- package/dist/utils/formDataUtils.d.ts +0 -22
- package/dist/utils/index.d.ts +0 -3
- package/dist/utils/schemaUtils.d.ts +0 -24
- package/dist/utils/validationUtils.d.ts +0 -26
- package/dist/vanilla-wrapper.d.ts +0 -35
- package/dist/widgets/date/DateWidget.d.ts +0 -4
- package/dist/widgets/enhanced/EnhancedWidgets.d.ts +0 -36
- package/dist/widgets/file/FileWidget.d.ts +0 -4
- package/dist/widgets/index.d.ts +0 -33
- package/dist/widgets/input/ColorWidget.d.ts +0 -4
- package/dist/widgets/input/RangeWidget.d.ts +0 -4
- package/dist/widgets/input/RatingWidget.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,318 +1,668 @@
|
|
|
1
1
|
# Extended Dynamic Forms
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
3
|
+
**A revolutionary extension library for [React JSON Schema Form (RJSF)](https://github.com/rjsf-team/react-jsonschema-form)** that transforms schema-driven forms into sophisticated, enterprise-ready applications with O(N) performance and professional UI components.
|
|
4
|
+
|
|
5
|
+
Built on top of the excellent RJSF v6 foundation, this library introduces the **Three Pillars v2 Architecture** for conditional logic, multi-step wizard orchestration, central event systems with webhook integration, and a comprehensive suite of 25+ Ant Design components.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/extended-dynamic-forms)
|
|
8
|
+
[](https://github.com/user/extended-dynamic-forms/blob/main/LICENSE)
|
|
9
|
+
[](https://github.com/rjsf-team/react-jsonschema-form)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](#standalone-javascript-support)
|
|
12
|
+
|
|
13
|
+
## Why Extended Dynamic Forms?
|
|
14
|
+
|
|
15
|
+
While [RJSF](https://github.com/rjsf-team/react-jsonschema-form) provides an excellent foundation for generating forms from JSON Schema, enterprise applications demand more sophisticated capabilities:
|
|
16
|
+
|
|
17
|
+
### 🚀 **Performance Revolution**
|
|
18
|
+
- **O(N) Conditional Logic**: Advanced Three Pillars v2 architecture vs traditional O(M×N) systems
|
|
19
|
+
- **Optimized Array Processing**: Per-item evaluation with dynamic array conditionals
|
|
20
|
+
- **Memory Efficient**: Immutable schema processing with intelligent caching
|
|
21
|
+
|
|
22
|
+
### 🎯 **Enterprise-Grade Features**
|
|
23
|
+
- **Complex Conditional Logic**: Multi-field dependencies, nested conditions, and dynamic validation
|
|
24
|
+
- **Multi-Step Workflows**: Automatic wizard detection with step-aware conditional logic
|
|
25
|
+
- **Real-Time Integration**: Production-ready webhooks with retry logic and error isolation
|
|
26
|
+
- **Professional UI**: Comprehensive suite of enhanced Ant Design components with consistent styling
|
|
27
|
+
|
|
28
|
+
### 🔄 **Developer Experience**
|
|
29
|
+
- **Dual Architecture Support**: Modern React hooks + vanilla JavaScript compatibility
|
|
30
|
+
- **Type-Safe Throughout**: Comprehensive TypeScript interfaces and validation
|
|
31
|
+
- **Rich Ecosystem**: Interactive playground with comprehensive real-world demos
|
|
32
|
+
- **Migration Ready**: Automated v1-to-v2 conversion utilities
|
|
33
|
+
|
|
34
|
+
This library transforms RJSF from a simple form generator into a complete enterprise form solution while preserving its declarative approach.
|
|
35
|
+
|
|
36
|
+
## Core Features
|
|
37
|
+
|
|
38
|
+
### 🚀 Three Pillars v2 Architecture (Recommended)
|
|
39
|
+
- **UI Logic Pillar**: Control form appearance with JSON Patch operations
|
|
40
|
+
- **Schema Logic Pillar**: Modify data structure when truly needed
|
|
41
|
+
- **Validation Logic Pillar**: Dynamic validation rules without schema changes
|
|
42
|
+
- **O(N) Performance**: Superior array handling vs v1's O(M×N)
|
|
43
|
+
- **React Hooks**: Modern, declarative API with `useConditionalUi`, `useConditionalSchema`, `useConditionalValidation`
|
|
44
|
+
|
|
45
|
+
### 🎯 Declarative Conditional Logic
|
|
46
|
+
- Define complex form behavior without code
|
|
47
|
+
- Dynamic array support with per-item evaluation
|
|
48
|
+
- Compatible with `json-rules-engine` format
|
|
49
|
+
- Built-in migration utilities from v1 to v2
|
|
50
|
+
|
|
51
|
+
For example, you can integrate existing business logic:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { engine } from './rules-engine'; // Your configured json-rules-engine instance
|
|
55
|
+
|
|
56
|
+
const uiRules: UiRule[] = [{
|
|
57
|
+
name: 'Apply business logic for premium users',
|
|
58
|
+
// The condition can be a promise-based async function
|
|
59
|
+
condition: async (formData) => {
|
|
60
|
+
const { events } = await engine.run(formData);
|
|
61
|
+
return events.some(event => event.type === 'show-premium-feature');
|
|
62
|
+
},
|
|
63
|
+
effect: [...]
|
|
64
|
+
}];
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 🧙♂️ Multi-Step Wizard Forms
|
|
68
|
+
- Automatic step detection from schema structure
|
|
69
|
+
- Step-aware conditional logic
|
|
70
|
+
- Built-in progress indicators and navigation
|
|
71
|
+
- Per-step validation
|
|
72
|
+
|
|
73
|
+
**Step-Aware Conditional Logic**
|
|
74
|
+
|
|
75
|
+
The `WizardForm` automatically scopes conditional logic to the current step. Use v2 `UiRule`s with field prefixes:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// Define steps with prefixed field names to avoid collisions
|
|
79
|
+
const schema = {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
STEP_PERSONAL: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
title: 'Personal Information',
|
|
85
|
+
properties: {
|
|
86
|
+
isUSCitizen: { type: 'boolean', title: 'Are you a US Citizen?' },
|
|
87
|
+
ssn: { type: 'string', title: 'Social Security Number' }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ... other steps
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Use v2 UiRules to handle step-aware conditionals
|
|
95
|
+
const uiRules: UiRule[] = [{
|
|
96
|
+
name: 'Show SSN only for US Citizens in Personal step',
|
|
97
|
+
condition: (formData) => !formData.STEP_PERSONAL?.isUSCitizen,
|
|
98
|
+
effect: [
|
|
99
|
+
{ op: 'add', path: '/STEP_PERSONAL/properties/ssn/ui:widget', value: 'hidden' }
|
|
100
|
+
]
|
|
101
|
+
}];
|
|
102
|
+
|
|
103
|
+
// The WizardForm handles the step isolation automatically
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 🔗 Central Event Orchestration
|
|
107
|
+
- Unified event system for all field interactions
|
|
108
|
+
- Webhook integration with retry logic
|
|
109
|
+
- Real-time form analytics capabilities
|
|
110
|
+
- Debounced event processing
|
|
111
|
+
|
|
112
|
+
### 🎨 Ant Design Components
|
|
113
|
+
- Complete widget suite built with Ant Design v5
|
|
114
|
+
- Enhanced standard widgets with event integration
|
|
115
|
+
- Custom widgets: Rating, Color Picker, Range Slider
|
|
116
|
+
- Professional field components: Address, Phone, Currency
|
|
117
|
+
|
|
118
|
+
### ⚡ Form Lifecycle Hooks
|
|
119
|
+
- Intercept and modify form data at key stages
|
|
120
|
+
- `beforeSubmit`, `afterSubmit`, `beforeValidation`, `afterValidation`
|
|
121
|
+
- Ideal for data transformation, logging, or triggering side effects
|
|
122
|
+
|
|
123
|
+
### 📦 Additional Features
|
|
124
|
+
- **TypeScript**: Full type safety and IntelliSense with comprehensive interface definitions
|
|
125
|
+
- **Performance**: Optimized re-renders and memoization with intelligent caching
|
|
126
|
+
- **Extensible**: Add custom widgets, fields, and actions with clean APIs
|
|
127
|
+
- **Well-Tested**: Extensive test suite covering all major features and edge cases
|
|
128
|
+
- **Developer Experience**: Interactive playground with dozens of real-world demos
|
|
129
|
+
|
|
130
|
+
### 🌐 Standalone JavaScript Support
|
|
131
|
+
|
|
132
|
+
**Complete vanilla JavaScript compatibility** with a production-ready UMD bundle that includes React, Ant Design, and all dependencies:
|
|
133
|
+
|
|
134
|
+
```html
|
|
135
|
+
<!DOCTYPE html>
|
|
136
|
+
<html>
|
|
137
|
+
<head>
|
|
138
|
+
<script src="https://unpkg.com/extended-dynamic-forms/dist/standalone.umd.js"></script>
|
|
139
|
+
<link rel="stylesheet" href="https://unpkg.com/extended-dynamic-forms/dist/style.css">
|
|
140
|
+
</head>
|
|
141
|
+
<body>
|
|
142
|
+
<div id="form-container"></div>
|
|
143
|
+
|
|
144
|
+
<script>
|
|
145
|
+
const { createForm, createWizard } = ExtendedDynamicForms;
|
|
146
|
+
|
|
147
|
+
// Create a complete form without React knowledge
|
|
148
|
+
const form = createForm({
|
|
149
|
+
schema: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
name: { type: 'string', title: 'Full Name' },
|
|
153
|
+
email: { type: 'string', format: 'email', title: 'Email' }
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
container: document.getElementById('form-container'),
|
|
157
|
+
onSubmit: (data) => console.log('Form submitted:', data)
|
|
158
|
+
});
|
|
159
|
+
</script>
|
|
160
|
+
</body>
|
|
161
|
+
</html>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Perfect for:**
|
|
165
|
+
- WordPress/Drupal integrations
|
|
166
|
+
- Legacy applications
|
|
167
|
+
- CMS form builders
|
|
168
|
+
- No-build environments
|
|
169
|
+
- PHP/ASP.NET applications
|
|
170
|
+
|
|
171
|
+
## Credits & Acknowledgments
|
|
172
|
+
|
|
173
|
+
This library is built on top of [React JSON Schema Form (RJSF)](https://github.com/rjsf-team/react-jsonschema-form), an amazing open-source project that provides the core form generation engine. We extend RJSF's capabilities while maintaining full compatibility with its ecosystem.
|
|
174
|
+
|
|
175
|
+
Special thanks to the RJSF team and contributors for creating such a flexible and well-architected foundation.
|
|
20
176
|
|
|
21
177
|
## Installation
|
|
22
178
|
|
|
179
|
+
### React Projects
|
|
180
|
+
|
|
23
181
|
```bash
|
|
24
182
|
npm install extended-dynamic-forms
|
|
25
183
|
# or
|
|
26
184
|
yarn add extended-dynamic-forms
|
|
185
|
+
# or
|
|
186
|
+
pnpm add extended-dynamic-forms
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Standalone JavaScript (No Build Required)
|
|
190
|
+
|
|
191
|
+
```html
|
|
192
|
+
<!-- Production CDN -->
|
|
193
|
+
<script src="https://unpkg.com/extended-dynamic-forms@latest/dist/standalone.umd.js"></script>
|
|
194
|
+
<link rel="stylesheet" href="https://unpkg.com/extended-dynamic-forms@latest/dist/style.css">
|
|
195
|
+
|
|
196
|
+
<!-- Or download and host locally -->
|
|
197
|
+
<script src="./path/to/extended-dynamic-forms-standalone.js"></script>
|
|
27
198
|
```
|
|
28
199
|
|
|
29
200
|
### Peer Dependencies
|
|
30
201
|
|
|
31
|
-
|
|
202
|
+
**Current Version**: `0.1.17` - Optimized for **RJSF v6** with Three Pillars v2 architecture.
|
|
32
203
|
|
|
33
204
|
```json
|
|
34
205
|
{
|
|
35
206
|
"react": "^18.0.0",
|
|
36
207
|
"react-dom": "^18.0.0",
|
|
37
|
-
"
|
|
38
|
-
"@rjsf/utils": "^5.15.0",
|
|
39
|
-
"@rjsf/validator-ajv8": "^5.15.0",
|
|
40
|
-
"@rjsf/shadcn": "^0.1.0"
|
|
208
|
+
"antd": "^5.24.0"
|
|
41
209
|
}
|
|
42
210
|
```
|
|
43
211
|
|
|
212
|
+
**RJSF Dependencies** (automatically handled):
|
|
213
|
+
- `@rjsf/core`: ^6.0.0+
|
|
214
|
+
- `@rjsf/utils`: ^6.0.0+
|
|
215
|
+
- `@rjsf/validator-ajv8`: ^6.0.0+
|
|
216
|
+
- `@rjsf/antd`: ^6.0.0+
|
|
217
|
+
|
|
218
|
+
> **Development Note**: This library is currently developed against RJSF v6 and includes all necessary RJSF dependencies. No additional RJSF packages need to be installed separately.
|
|
219
|
+
|
|
220
|
+
**Additional Dependencies**:
|
|
221
|
+
- `dayjs`: ^1.11.13 (Date handling)
|
|
222
|
+
- `json-rules-engine-simplified`: ^0.2.0 (v1 conditional logic)
|
|
223
|
+
|
|
224
|
+
> **Note**: This library is specifically built for RJSF v6 to leverage the latest performance improvements and API enhancements. All examples and documentation assume RJSF v6.
|
|
225
|
+
|
|
44
226
|
## Setup
|
|
45
227
|
|
|
46
228
|
### CSS Requirements
|
|
47
229
|
|
|
48
|
-
This package uses
|
|
230
|
+
This package uses Ant Design components. Import the CSS in your application:
|
|
49
231
|
|
|
50
232
|
```tsx
|
|
51
233
|
// In your main entry file (e.g., App.tsx or index.tsx)
|
|
52
|
-
import 'extended-dynamic-forms/dist/
|
|
234
|
+
import 'extended-dynamic-forms/dist/style.css'; // Or your specific CSS entry point
|
|
53
235
|
```
|
|
54
236
|
|
|
55
|
-
|
|
237
|
+
### Theming & Customization
|
|
56
238
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
239
|
+
Since Extended Dynamic Forms uses Ant Design components, you can customize the appearance using Ant Design's powerful theming system:
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
import { ConfigProvider, theme } from 'antd';
|
|
243
|
+
import { ExtendedForm, WizardForm } from 'extended-dynamic-forms';
|
|
244
|
+
|
|
245
|
+
function App() {
|
|
246
|
+
return (
|
|
247
|
+
<ConfigProvider
|
|
248
|
+
theme={{
|
|
249
|
+
token: {
|
|
250
|
+
colorPrimary: '#00b96b',
|
|
251
|
+
borderRadius: 8,
|
|
252
|
+
fontSize: 16,
|
|
253
|
+
},
|
|
254
|
+
algorithm: theme.darkAlgorithm, // Optional: Use dark theme
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
<WizardForm
|
|
258
|
+
schema={schema}
|
|
259
|
+
uiSchema={uiSchema}
|
|
260
|
+
conditionals={conditionals}
|
|
261
|
+
/>
|
|
262
|
+
</ConfigProvider>
|
|
263
|
+
);
|
|
64
264
|
}
|
|
65
265
|
```
|
|
66
266
|
|
|
67
|
-
##
|
|
267
|
+
## Three Pillars v2 Architecture (Recommended Approach)
|
|
268
|
+
|
|
269
|
+
> ⚠️ **IMPORTANT**: The v1 conditional system is deprecated. We strongly recommend using the Three Pillars v2 architecture for all new implementations.
|
|
68
270
|
|
|
69
|
-
|
|
271
|
+
The Three Pillars v2 system provides superior performance, cleaner separation of concerns, and better maintainability. It consists of three independent pillars:
|
|
272
|
+
|
|
273
|
+
### 1. UI Logic Pillar (`useConditionalUi`)
|
|
274
|
+
|
|
275
|
+
Controls form appearance using JSON Patch operations:
|
|
70
276
|
|
|
71
277
|
```tsx
|
|
72
|
-
import
|
|
73
|
-
import {
|
|
278
|
+
// Note: v2 conditionals require deep import for optimal tree-shaking
|
|
279
|
+
import { useConditionalUi, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
74
280
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
name:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
281
|
+
// Define UI rules with JSON Patch operations
|
|
282
|
+
const uiRules: UiRule[] = [
|
|
283
|
+
{
|
|
284
|
+
name: 'Show shipping address when required',
|
|
285
|
+
condition: (formData) => formData.requiresShipping === true,
|
|
286
|
+
effect: [{ op: 'remove', path: '/shippingAddress/ui:widget' }],
|
|
287
|
+
order: 1
|
|
82
288
|
},
|
|
83
|
-
|
|
84
|
-
|
|
289
|
+
{
|
|
290
|
+
name: 'Dynamic country fields',
|
|
291
|
+
condition: (formData) => formData.shippingAddress?.country === 'USA',
|
|
292
|
+
effect: [
|
|
293
|
+
{ op: 'remove', path: '/shippingAddress/state/ui:widget' },
|
|
294
|
+
{ op: 'add', path: '/shippingAddress/province/ui:widget', value: 'hidden' }
|
|
295
|
+
],
|
|
296
|
+
order: 2
|
|
297
|
+
}
|
|
298
|
+
];
|
|
85
299
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
'ui:widget': '
|
|
89
|
-
'ui:
|
|
90
|
-
|
|
91
|
-
favoriteColor: {
|
|
92
|
-
'ui:widget': 'color'
|
|
300
|
+
const baseUiSchema = {
|
|
301
|
+
shippingAddress: {
|
|
302
|
+
'ui:widget': 'hidden', // Initially hidden
|
|
303
|
+
state: { 'ui:widget': 'text' },
|
|
304
|
+
province: { 'ui:widget': 'hidden' } // Initially hidden
|
|
93
305
|
}
|
|
94
306
|
};
|
|
95
307
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
};
|
|
104
|
-
|
|
308
|
+
// In your component
|
|
309
|
+
function MyForm() {
|
|
310
|
+
const [formData, setFormData] = useState({});
|
|
311
|
+
|
|
312
|
+
// IMPORTANT: Define rules outside render or memoize them!
|
|
313
|
+
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
314
|
+
|
|
105
315
|
return (
|
|
106
316
|
<ExtendedForm
|
|
107
317
|
schema={schema}
|
|
108
|
-
uiSchema={
|
|
109
|
-
|
|
110
|
-
|
|
318
|
+
uiSchema={conditionalUiSchema}
|
|
319
|
+
formData={formData}
|
|
320
|
+
onChange={({ formData }) => setFormData(formData)}
|
|
111
321
|
/>
|
|
112
322
|
);
|
|
113
323
|
}
|
|
114
324
|
```
|
|
115
325
|
|
|
116
|
-
###
|
|
326
|
+
### 2. Schema Logic Pillar (`useConditionalSchema`)
|
|
327
|
+
|
|
328
|
+
Modifies JSON Schema structure (use sparingly - prefer UI or Validation pillars):
|
|
117
329
|
|
|
118
330
|
```tsx
|
|
119
|
-
import
|
|
120
|
-
import { ExtendedForm, WebhookConfig } from 'extended-dynamic-forms';
|
|
331
|
+
import { useConditionalSchema, SchemaRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
121
332
|
|
|
122
|
-
const
|
|
333
|
+
const schemaRules: SchemaRule[] = [
|
|
123
334
|
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
335
|
+
name: 'Add premium fields',
|
|
336
|
+
condition: (formData) => formData.accountType === 'premium',
|
|
337
|
+
effect: [
|
|
338
|
+
{
|
|
339
|
+
op: 'add',
|
|
340
|
+
path: '/properties/premiumFeatures',
|
|
341
|
+
value: {
|
|
342
|
+
type: 'object',
|
|
343
|
+
properties: {
|
|
344
|
+
priority: { type: 'string', enum: ['high', 'medium', 'low'] }
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
];
|
|
351
|
+
|
|
352
|
+
const conditionalSchema = useConditionalSchema(schemaRules, baseSchema, formData);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 3. Validation Logic Pillar (`useConditionalValidation`)
|
|
356
|
+
|
|
357
|
+
Adds conditional validation without modifying schema:
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
import { useMemo } from 'react';
|
|
361
|
+
import { useConditionalValidation, ValidationRule, createCustomValidate } from 'extended-dynamic-forms/conditionals/v2';
|
|
362
|
+
|
|
363
|
+
const validationRules: ValidationRule[] = [
|
|
364
|
+
{
|
|
365
|
+
name: 'Require consent for minors',
|
|
366
|
+
condition: (formData) => formData.age < 18,
|
|
367
|
+
effect: {
|
|
368
|
+
type: 'validation',
|
|
369
|
+
validate: (errors, formData) => {
|
|
370
|
+
if (!formData.parentConsent) {
|
|
371
|
+
errors.parentConsent.addError('Parent consent is required for minors');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
129
374
|
}
|
|
130
375
|
}
|
|
131
376
|
];
|
|
132
377
|
|
|
133
|
-
|
|
378
|
+
// In your component
|
|
379
|
+
function MyForm({ schema }) {
|
|
380
|
+
const [formData, setFormData] = useState({});
|
|
381
|
+
|
|
382
|
+
// 1. Get the conditional validation configuration
|
|
383
|
+
const conditionalValidationConfig = useConditionalValidation(validationRules, {}, formData);
|
|
384
|
+
|
|
385
|
+
// 2. Create the memoized validate function
|
|
386
|
+
const customValidate = useMemo(
|
|
387
|
+
() => createCustomValidate(conditionalValidationConfig),
|
|
388
|
+
[conditionalValidationConfig]
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
// 3. Use with ExtendedForm
|
|
134
392
|
return (
|
|
135
393
|
<ExtendedForm
|
|
136
394
|
schema={schema}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.log('Form submitted:', formData);
|
|
141
|
-
}}
|
|
142
|
-
onFieldFocus={(event) => {
|
|
143
|
-
console.log('Field focused:', event.fieldId);
|
|
144
|
-
}}
|
|
395
|
+
formData={formData}
|
|
396
|
+
onChange={({ formData }) => setFormData(formData)}
|
|
397
|
+
customValidate={customValidate}
|
|
145
398
|
/>
|
|
146
399
|
);
|
|
147
400
|
}
|
|
148
401
|
```
|
|
149
402
|
|
|
150
|
-
|
|
403
|
+
### Dynamic Arrays with O(N) Performance
|
|
404
|
+
|
|
405
|
+
#### **The Performance Revolution: `createDynamicArrayItemsUiSchema`**
|
|
406
|
+
|
|
407
|
+
One of the most significant breakthroughs in v2 is the `createDynamicArrayItemsUiSchema` function, which enables **true O(N) performance** for array conditionals. This is a fundamental improvement over traditional form libraries that struggle with dynamic array logic.
|
|
408
|
+
|
|
409
|
+
**How It Works:**
|
|
410
|
+
- **Per-Item Evaluation**: Each array item's conditionals are evaluated independently during render
|
|
411
|
+
- **No Rule Explosion**: Unlike v1's wildcard expansion, no rules are multiplied by array size
|
|
412
|
+
- **React-Optimized**: Leverages React's rendering cycle for optimal performance
|
|
151
413
|
|
|
152
|
-
### Color Widget
|
|
153
414
|
```tsx
|
|
415
|
+
import { createDynamicArrayItemsUiSchema } from 'extended-dynamic-forms/conditionals/v2';
|
|
416
|
+
|
|
417
|
+
// Define per-item conditional logic - this function runs for each array item
|
|
418
|
+
const directorItemConditions = (director, index) => ({
|
|
419
|
+
shareholdingPercentage: {
|
|
420
|
+
'ui:widget': director.hasShareholding ? 'percentage' : 'hidden'
|
|
421
|
+
},
|
|
422
|
+
tfn: {
|
|
423
|
+
'ui:widget': director.isAustralianResident ? 'text' : 'hidden'
|
|
424
|
+
},
|
|
425
|
+
passport: {
|
|
426
|
+
'ui:widget': !director.isAustralianResident ? 'text' : 'hidden'
|
|
427
|
+
},
|
|
428
|
+
// Add visual indicator for the chairman
|
|
429
|
+
'ui:classNames': index === 0 ? 'chairman-director' : 'regular-director'
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Apply to your array field
|
|
154
433
|
const uiSchema = {
|
|
155
|
-
|
|
156
|
-
'ui:
|
|
434
|
+
directors: {
|
|
435
|
+
'ui:title': 'Company Directors',
|
|
436
|
+
'ui:description': 'Add all company directors and their details',
|
|
437
|
+
items: createDynamicArrayItemsUiSchema(directorItemConditions)
|
|
157
438
|
}
|
|
158
439
|
};
|
|
159
440
|
```
|
|
160
441
|
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
minDate: '2000-01-01',
|
|
169
|
-
maxDate: '2025-12-31'
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
};
|
|
442
|
+
**Performance Comparison:**
|
|
443
|
+
```typescript
|
|
444
|
+
// ❌ Traditional approach: O(M×N) - Rules × Array Items
|
|
445
|
+
// With 10 rules and 50 directors = 500 evaluations
|
|
446
|
+
|
|
447
|
+
// ✅ v2 approach: O(N) - Per-item evaluation
|
|
448
|
+
// With 50 directors = 50 evaluations (regardless of rule complexity)
|
|
173
449
|
```
|
|
174
450
|
|
|
175
|
-
|
|
451
|
+
**Advanced Patterns:**
|
|
176
452
|
```tsx
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
'ui:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
453
|
+
// Complex business logic with multiple conditions
|
|
454
|
+
const productItemConditions = (product, index) => {
|
|
455
|
+
const baseConfig = {
|
|
456
|
+
price: { 'ui:widget': 'currency' },
|
|
457
|
+
quantity: { 'ui:widget': 'number' }
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Category-specific fields
|
|
461
|
+
if (product.category === 'software') {
|
|
462
|
+
baseConfig.licenseType = { 'ui:widget': 'select' };
|
|
463
|
+
baseConfig.maintenancePeriod = { 'ui:widget': 'hidden' };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Subscription-specific logic
|
|
467
|
+
if (product.billingType === 'subscription') {
|
|
468
|
+
baseConfig.recurringPrice = { 'ui:widget': 'currency' };
|
|
469
|
+
baseConfig.billingCycle = { 'ui:widget': 'select' };
|
|
184
470
|
}
|
|
471
|
+
|
|
472
|
+
return baseConfig;
|
|
185
473
|
};
|
|
186
474
|
```
|
|
187
475
|
|
|
188
|
-
###
|
|
476
|
+
### Performance Best Practices
|
|
477
|
+
|
|
478
|
+
> ⚠️ **Critical**: Always define rules and base schemas outside render or memoize them!
|
|
479
|
+
|
|
189
480
|
```tsx
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
481
|
+
// ❌ BAD - Creates new objects every render
|
|
482
|
+
function MyForm() {
|
|
483
|
+
const conditionalUiSchema = useConditionalUi(
|
|
484
|
+
[{ condition: () => true, effect: [...] }], // New array every render!
|
|
485
|
+
{ field: { 'ui:widget': 'text' } }, // New object every render!
|
|
486
|
+
formData
|
|
487
|
+
);
|
|
488
|
+
}
|
|
198
489
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
490
|
+
// ✅ GOOD - Stable references
|
|
491
|
+
const rules = [{ condition: () => true, effect: [...] }];
|
|
492
|
+
const baseUiSchema = { field: { 'ui:widget': 'text' } };
|
|
493
|
+
|
|
494
|
+
function MyForm() {
|
|
495
|
+
const conditionalUiSchema = useConditionalUi(rules, baseUiSchema, formData);
|
|
496
|
+
}
|
|
204
497
|
```
|
|
205
498
|
|
|
206
|
-
###
|
|
499
|
+
### Migration from v1 to v2
|
|
500
|
+
|
|
207
501
|
```tsx
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
502
|
+
import {
|
|
503
|
+
migrateV1ToUiRules,
|
|
504
|
+
generateMigrationReport,
|
|
505
|
+
analyzeRules
|
|
506
|
+
} from 'extended-dynamic-forms/conditionals/v2';
|
|
507
|
+
|
|
508
|
+
// Analyze existing rules
|
|
509
|
+
const analysis = analyzeRules(v1Rules);
|
|
510
|
+
|
|
511
|
+
// Auto-migrate UI rules
|
|
512
|
+
const { rules: migratedRules, report } = migrateV1ToUiRules(v1Rules);
|
|
513
|
+
|
|
514
|
+
// Generate detailed migration report
|
|
515
|
+
const migrationReport = generateMigrationReport(v1Rules, {
|
|
516
|
+
includeExamples: true,
|
|
517
|
+
outputFormat: 'markdown'
|
|
518
|
+
});
|
|
216
519
|
```
|
|
217
520
|
|
|
218
|
-
##
|
|
521
|
+
## Quick Start
|
|
522
|
+
|
|
523
|
+
### Simple Wizard Form (Recommended)
|
|
524
|
+
|
|
525
|
+
The `WizardForm` component automatically detects multi-step forms and handles navigation. Use it with Three Pillars v2:
|
|
219
526
|
|
|
220
|
-
### Address Field
|
|
221
527
|
```tsx
|
|
528
|
+
import { useState } from 'react';
|
|
529
|
+
import { WizardForm } from 'extended-dynamic-forms';
|
|
530
|
+
import { useConditionalUi, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
531
|
+
import schema from './schema.json';
|
|
532
|
+
import baseUiSchema from './ui-schema.json';
|
|
533
|
+
import uiRules from './ui-rules'; // Your v2 UI rules
|
|
534
|
+
import webhooks from './webhooks.json';
|
|
535
|
+
|
|
536
|
+
function App() {
|
|
537
|
+
const [formData, setFormData] = useState({});
|
|
538
|
+
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<WizardForm
|
|
542
|
+
schema={schema}
|
|
543
|
+
uiSchema={conditionalUiSchema}
|
|
544
|
+
formData={formData}
|
|
545
|
+
onChange={({ formData }) => setFormData(formData)}
|
|
546
|
+
webhooks={webhooks}
|
|
547
|
+
onSubmit={(data) => console.log('Submitted:', data.formData)}
|
|
548
|
+
/>
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### Basic Form with Three Pillars v2
|
|
554
|
+
|
|
555
|
+
```tsx
|
|
556
|
+
import { ExtendedForm } from 'extended-dynamic-forms';
|
|
557
|
+
import { useConditionalUi, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
558
|
+
|
|
222
559
|
const schema = {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
state: { type: 'string' },
|
|
229
|
-
zip: { type: 'string' },
|
|
230
|
-
country: { type: 'string' }
|
|
231
|
-
}
|
|
560
|
+
type: 'object',
|
|
561
|
+
properties: {
|
|
562
|
+
isStudent: { type: 'boolean', title: 'Are you a student?' },
|
|
563
|
+
studentId: { type: 'string', title: 'Student ID' },
|
|
564
|
+
school: { type: 'string', title: 'School Name' }
|
|
232
565
|
}
|
|
233
566
|
};
|
|
234
567
|
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
568
|
+
const baseUiSchema = {
|
|
569
|
+
studentId: { 'ui:widget': 'text' },
|
|
570
|
+
school: { 'ui:widget': 'text' }
|
|
239
571
|
};
|
|
572
|
+
|
|
573
|
+
// Define UI rules outside component
|
|
574
|
+
const uiRules: UiRule[] = [
|
|
575
|
+
{
|
|
576
|
+
name: 'Show student fields when student',
|
|
577
|
+
condition: (formData) => !formData.isStudent,
|
|
578
|
+
effect: [
|
|
579
|
+
{ op: 'add', path: '/studentId/ui:widget', value: 'hidden' },
|
|
580
|
+
{ op: 'add', path: '/school/ui:widget', value: 'hidden' }
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
];
|
|
584
|
+
|
|
585
|
+
function App() {
|
|
586
|
+
const [formData, setFormData] = useState({});
|
|
587
|
+
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<ExtendedForm
|
|
591
|
+
schema={schema}
|
|
592
|
+
uiSchema={conditionalUiSchema}
|
|
593
|
+
formData={formData}
|
|
594
|
+
onChange={({ formData }) => setFormData(formData)}
|
|
595
|
+
/>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
240
598
|
```
|
|
241
599
|
|
|
242
|
-
|
|
600
|
+
## Custom Widgets
|
|
601
|
+
|
|
602
|
+
### Available Widgets
|
|
603
|
+
|
|
604
|
+
The library includes enhanced versions of all standard RJSF widgets plus custom ones:
|
|
605
|
+
|
|
606
|
+
#### Custom Widgets
|
|
607
|
+
|
|
243
608
|
```tsx
|
|
609
|
+
// Color Picker
|
|
244
610
|
const uiSchema = {
|
|
245
|
-
|
|
246
|
-
'ui:
|
|
611
|
+
favoriteColor: {
|
|
612
|
+
'ui:widget': 'color'
|
|
247
613
|
}
|
|
248
614
|
};
|
|
249
|
-
```
|
|
250
615
|
|
|
251
|
-
|
|
252
|
-
```tsx
|
|
616
|
+
// Rating Widget
|
|
253
617
|
const uiSchema = {
|
|
254
|
-
|
|
255
|
-
'ui:
|
|
618
|
+
satisfaction: {
|
|
619
|
+
'ui:widget': 'rating',
|
|
256
620
|
'ui:options': {
|
|
257
|
-
|
|
258
|
-
symbol: '€'
|
|
621
|
+
max: 10 // default is 5
|
|
259
622
|
}
|
|
260
623
|
}
|
|
261
624
|
};
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## Array Fields
|
|
265
|
-
|
|
266
|
-
Extended Dynamic Forms provides custom-styled array field templates using shadcn/ui components. Arrays automatically get styled add/remove buttons, reordering controls, and proper visual hierarchy.
|
|
267
625
|
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
626
|
+
// Range Slider
|
|
270
627
|
const schema = {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
lastName: { type: 'string', title: 'Last Name' },
|
|
281
|
-
email: { type: 'string', format: 'email', title: 'Email' }
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
628
|
+
volume: {
|
|
629
|
+
type: 'number',
|
|
630
|
+
minimum: 0,
|
|
631
|
+
maximum: 100
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
const uiSchema = {
|
|
635
|
+
volume: {
|
|
636
|
+
'ui:widget': 'range'
|
|
285
637
|
}
|
|
286
638
|
};
|
|
287
639
|
|
|
640
|
+
// File Upload
|
|
288
641
|
const uiSchema = {
|
|
289
|
-
|
|
642
|
+
documents: {
|
|
643
|
+
'ui:widget': 'file',
|
|
290
644
|
'ui:options': {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
removable: true // Enable remove button
|
|
645
|
+
accept: '.pdf,.doc,.docx',
|
|
646
|
+
multiple: true
|
|
294
647
|
}
|
|
295
648
|
}
|
|
296
649
|
};
|
|
297
650
|
```
|
|
298
651
|
|
|
299
|
-
|
|
300
|
-
- 🎨 **Styled with shadcn/ui Card components**: Clean, modern appearance
|
|
301
|
-
- ➕ **Add button**: Prominent "Add [Item]" button at the bottom
|
|
302
|
-
- 🗑️ **Remove buttons**: Individual remove buttons for each item
|
|
303
|
-
- 🔄 **Reordering**: Move items up/down with chevron buttons
|
|
304
|
-
- 📱 **Responsive**: Mobile-friendly controls
|
|
652
|
+
#### Enhanced Standard Widgets
|
|
305
653
|
|
|
306
|
-
|
|
654
|
+
All standard RJSF widgets are enhanced with:
|
|
655
|
+
- **Event Integration**: Automatic integration with the central event system
|
|
656
|
+
- **Ant Design Styling**: Modern, consistent appearance
|
|
657
|
+
- **Error States**: Proper error indication and messaging
|
|
658
|
+
- **Accessibility**: Enhanced ARIA support
|
|
307
659
|
|
|
308
660
|
## Central Event Orchestration System
|
|
309
661
|
|
|
310
|
-
The library features a comprehensive event orchestration system that captures all form field events and enables webhook integration.
|
|
311
|
-
|
|
312
662
|
### Field-Level Event Handling
|
|
313
663
|
|
|
314
664
|
```tsx
|
|
315
|
-
import { ExtendedForm, FormFieldEvent } from 'extended-dynamic-forms';
|
|
665
|
+
import { ExtendedForm, type FormFieldEvent } from 'extended-dynamic-forms';
|
|
316
666
|
|
|
317
667
|
<ExtendedForm
|
|
318
668
|
schema={schema}
|
|
@@ -322,29 +672,15 @@ import { ExtendedForm, FormFieldEvent } from 'extended-dynamic-forms';
|
|
|
322
672
|
// Field-level event handlers
|
|
323
673
|
onFieldFocus={(event: FormFieldEvent) => {
|
|
324
674
|
console.log(`Field ${event.fieldId} focused:`, event.fieldValue);
|
|
325
|
-
// Track user interactions for analytics
|
|
326
675
|
}}
|
|
327
676
|
|
|
328
677
|
onFieldChange={(event: FormFieldEvent) => {
|
|
329
678
|
console.log(`Field ${event.fieldId} changed:`, event.fieldValue);
|
|
330
679
|
// Real-time validation or processing
|
|
331
|
-
if (event.fieldId === 'root_email') {
|
|
332
|
-
validateEmailRealTime(event.fieldValue);
|
|
333
|
-
}
|
|
334
680
|
}}
|
|
335
681
|
|
|
336
682
|
onFieldBlur={(event: FormFieldEvent) => {
|
|
337
683
|
console.log(`Field ${event.fieldId} blurred:`, event.fieldValue);
|
|
338
|
-
// Format field values or trigger validation
|
|
339
|
-
}}
|
|
340
|
-
|
|
341
|
-
// Traditional form-level events (still supported)
|
|
342
|
-
beforeSubmit={async (data) => {
|
|
343
|
-
return { ...data, timestamp: Date.now() };
|
|
344
|
-
}}
|
|
345
|
-
|
|
346
|
-
afterSubmit={async (data) => {
|
|
347
|
-
console.log('Form submitted:', data);
|
|
348
684
|
}}
|
|
349
685
|
/>
|
|
350
686
|
```
|
|
@@ -354,7 +690,7 @@ import { ExtendedForm, FormFieldEvent } from 'extended-dynamic-forms';
|
|
|
354
690
|
Configure multiple webhook endpoints to receive real-time form events:
|
|
355
691
|
|
|
356
692
|
```tsx
|
|
357
|
-
import { ExtendedForm, WebhookConfig } from 'extended-dynamic-forms';
|
|
693
|
+
import { ExtendedForm, type WebhookConfig } from 'extended-dynamic-forms';
|
|
358
694
|
|
|
359
695
|
const webhookConfigs: WebhookConfig[] = [
|
|
360
696
|
{
|
|
@@ -366,17 +702,7 @@ const webhookConfigs: WebhookConfig[] = [
|
|
|
366
702
|
timeout: 5000, // 5 second timeout
|
|
367
703
|
headers: {
|
|
368
704
|
'Authorization': 'Bearer your-api-token',
|
|
369
|
-
'X-Form-ID': 'contact-form'
|
|
370
|
-
'Content-Type': 'application/json'
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
url: 'https://analytics.yourapp.com/field-interactions',
|
|
375
|
-
events: ['focus'], // Track focus events for analytics
|
|
376
|
-
debounceMs: 0, // No debouncing for real-time analytics
|
|
377
|
-
retries: 1,
|
|
378
|
-
headers: {
|
|
379
|
-
'X-Analytics-Key': 'your-analytics-key'
|
|
705
|
+
'X-Form-ID': 'contact-form'
|
|
380
706
|
}
|
|
381
707
|
}
|
|
382
708
|
];
|
|
@@ -385,408 +711,390 @@ const webhookConfigs: WebhookConfig[] = [
|
|
|
385
711
|
schema={schema}
|
|
386
712
|
uiSchema={uiSchema}
|
|
387
713
|
webhooks={webhookConfigs}
|
|
388
|
-
|
|
389
|
-
onFieldChange={(event) => {
|
|
390
|
-
// Local processing alongside webhooks
|
|
391
|
-
if (event.fieldId === 'root_email') {
|
|
392
|
-
setEmailValid(validateEmail(event.fieldValue));
|
|
393
|
-
}
|
|
394
|
-
}}
|
|
395
714
|
/>
|
|
396
715
|
```
|
|
397
716
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
Your webhook endpoints will receive payloads in this format:
|
|
401
|
-
|
|
402
|
-
```json
|
|
403
|
-
{
|
|
404
|
-
"event": {
|
|
405
|
-
"eventType": "change",
|
|
406
|
-
"fieldId": "root_email",
|
|
407
|
-
"fieldValue": "user@example.com",
|
|
408
|
-
"schema": {
|
|
409
|
-
"type": "string",
|
|
410
|
-
"format": "email",
|
|
411
|
-
"title": "Email Address"
|
|
412
|
-
},
|
|
413
|
-
"formData": {
|
|
414
|
-
"email": "user@example.com",
|
|
415
|
-
"name": "John Doe"
|
|
416
|
-
},
|
|
417
|
-
"timestamp": 1678901234567,
|
|
418
|
-
"fieldPath": ["email"]
|
|
419
|
-
},
|
|
420
|
-
"timestamp": 1678901234567,
|
|
421
|
-
"source": "extended-dynamic-forms"
|
|
422
|
-
}
|
|
423
|
-
```
|
|
717
|
+
## V1 Conditional System (Deprecated)
|
|
424
718
|
|
|
425
|
-
|
|
719
|
+
> ⚠️ **DEPRECATED**: The v1 conditional system is deprecated and will be removed in a future version. Use the Three Pillars v2 system for all new implementations.
|
|
426
720
|
|
|
427
|
-
|
|
428
|
-
- **Retry Logic**: Automatic retry with exponential backoff for failed requests
|
|
429
|
-
- **Error Isolation**: Webhook failures don't block form functionality
|
|
430
|
-
- **Event Filtering**: Configure which events trigger each webhook
|
|
431
|
-
- **Queue Management**: Asynchronous processing with proper ordering
|
|
432
|
-
- **Custom Headers**: Add authentication, tracking, or custom metadata
|
|
721
|
+
The v1 system uses imperative engines with O(M×N) performance for arrays. While still functional, it has significant limitations compared to v2.
|
|
433
722
|
|
|
434
|
-
|
|
723
|
+
### Basic V1 Usage (Not Recommended)
|
|
435
724
|
|
|
436
725
|
```tsx
|
|
437
|
-
import {
|
|
438
|
-
|
|
439
|
-
// Vertical Layout
|
|
440
|
-
<VerticalLayout spacing="medium">
|
|
441
|
-
<input />
|
|
442
|
-
<button />
|
|
443
|
-
</VerticalLayout>
|
|
726
|
+
import { ExtendedForm } from 'extended-dynamic-forms';
|
|
444
727
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
728
|
+
const conditionals = {
|
|
729
|
+
rules: [
|
|
730
|
+
{
|
|
731
|
+
conditions: { isStudent: true },
|
|
732
|
+
event: { type: "require", params: { field: ["studentId", "school"] } }
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
conditions: { isStudent: false },
|
|
736
|
+
event: { type: "remove", params: { field: ["studentId", "school"] } }
|
|
737
|
+
}
|
|
738
|
+
]
|
|
739
|
+
};
|
|
450
740
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
</GridLayout>
|
|
741
|
+
<ExtendedForm
|
|
742
|
+
schema={schema}
|
|
743
|
+
uiSchema={uiSchema}
|
|
744
|
+
conditionals={conditionals}
|
|
745
|
+
/>
|
|
457
746
|
```
|
|
458
747
|
|
|
459
|
-
|
|
748
|
+
### Why Migrate to v2?
|
|
460
749
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
events: ['focus', 'blur', 'change'],
|
|
467
|
-
debounceMs: 100,
|
|
468
|
-
headers: { 'X-Analytics-Token': 'your-token' }
|
|
469
|
-
}
|
|
470
|
-
];
|
|
750
|
+
- **Performance**: O(N) vs O(M×N) for arrays
|
|
751
|
+
- **Maintainability**: Clear separation of concerns
|
|
752
|
+
- **Type Safety**: Full TypeScript support
|
|
753
|
+
- **Predictability**: No complex rule interactions
|
|
754
|
+
- **Modern React**: Hooks-based architecture
|
|
471
755
|
|
|
472
|
-
|
|
473
|
-
```
|
|
756
|
+
## API Reference
|
|
474
757
|
|
|
475
|
-
###
|
|
476
|
-
```tsx
|
|
477
|
-
<ExtendedForm
|
|
478
|
-
schema={schema}
|
|
479
|
-
onFieldChange={(event) => {
|
|
480
|
-
// Auto-save to localStorage or server
|
|
481
|
-
localStorage.setItem(`form_${event.fieldId}`, event.fieldValue);
|
|
482
|
-
}}
|
|
483
|
-
webhooks={[{
|
|
484
|
-
url: 'https://api.yourapp.com/autosave',
|
|
485
|
-
events: ['change'],
|
|
486
|
-
debounceMs: 2000 // Save every 2 seconds
|
|
487
|
-
}]}
|
|
488
|
-
/>
|
|
489
|
-
```
|
|
758
|
+
### ExtendedFormProps
|
|
490
759
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
schema
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
760
|
+
```typescript
|
|
761
|
+
interface ExtendedFormProps<T = any, S extends RJSFSchema = RJSFSchema, F = any> extends FormProps<T, S, F> {
|
|
762
|
+
// Standard RJSF props
|
|
763
|
+
schema: S;
|
|
764
|
+
uiSchema?: UiSchema<T, S, F>;
|
|
765
|
+
formData?: T;
|
|
766
|
+
onSubmit?: (data: IChangeEvent<T, S, F>, event?: React.FormEvent<HTMLFormElement>) => void;
|
|
767
|
+
onChange?: (data: IChangeEvent<T, S, F>) => void;
|
|
768
|
+
|
|
769
|
+
// Field-level event handlers
|
|
770
|
+
onFieldFocus?: (event: FormFieldEvent) => Promise<void> | void;
|
|
771
|
+
onFieldBlur?: (event: FormFieldEvent) => Promise<void> | void;
|
|
772
|
+
onFieldChange?: (event: FormFieldEvent) => Promise<void> | void;
|
|
773
|
+
|
|
774
|
+
// Webhook configuration
|
|
775
|
+
webhooks?: WebhookConfig[];
|
|
776
|
+
|
|
777
|
+
// Conditional logic (v1 - deprecated)
|
|
778
|
+
conditionals?: ConditionalConfig;
|
|
779
|
+
|
|
780
|
+
// Form lifecycle events
|
|
781
|
+
beforeSubmit?: (data: T) => Promise<T> | T;
|
|
782
|
+
afterSubmit?: (data: T) => Promise<void> | void;
|
|
783
|
+
beforeValidation?: (data: T) => Promise<T> | T;
|
|
784
|
+
afterValidation?: (data: T, errors: RJSFValidationError[]) => Promise<void> | void;
|
|
785
|
+
}
|
|
507
786
|
```
|
|
508
787
|
|
|
509
|
-
###
|
|
510
|
-
```tsx
|
|
511
|
-
<ExtendedForm
|
|
512
|
-
schema={schema}
|
|
513
|
-
onFieldChange={(event) => {
|
|
514
|
-
// Show additional fields based on user input
|
|
515
|
-
if (event.fieldId === 'root_country' && event.fieldValue === 'US') {
|
|
516
|
-
setShowStateField(true);
|
|
517
|
-
}
|
|
518
|
-
}}
|
|
519
|
-
/>
|
|
520
|
-
```
|
|
788
|
+
### Three Pillars v2 Hooks
|
|
521
789
|
|
|
522
|
-
|
|
790
|
+
```typescript
|
|
791
|
+
// UI Logic Pillar
|
|
792
|
+
function useConditionalUi(
|
|
793
|
+
rules: UiRule[],
|
|
794
|
+
baseUiSchema: UiSchema,
|
|
795
|
+
formData: any
|
|
796
|
+
): UiSchema
|
|
797
|
+
|
|
798
|
+
// Schema Logic Pillar
|
|
799
|
+
function useConditionalSchema(
|
|
800
|
+
rules: SchemaRule[],
|
|
801
|
+
baseSchema: RJSFSchema,
|
|
802
|
+
formData: any
|
|
803
|
+
): RJSFSchema
|
|
804
|
+
|
|
805
|
+
// Validation Logic Pillar
|
|
806
|
+
function useConditionalValidation(
|
|
807
|
+
rules: ValidationRule[],
|
|
808
|
+
baseConfig: ValidationConfig,
|
|
809
|
+
formData: any
|
|
810
|
+
): ValidationConfig
|
|
811
|
+
```
|
|
523
812
|
|
|
524
|
-
|
|
525
|
-
- Show/hide fields based on other field values
|
|
526
|
-
- Make fields required/optional conditionally
|
|
527
|
-
- Enable/disable fields based on complex conditions
|
|
528
|
-
- Support multiple conditions with AND/OR logic
|
|
813
|
+
## Real-World Use Cases
|
|
529
814
|
|
|
530
|
-
###
|
|
815
|
+
### 🏢 Enterprise Business Applications
|
|
531
816
|
|
|
817
|
+
**Australian Company Registration** (Featured Demo)
|
|
532
818
|
```tsx
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
enum: ['personal', 'business'],
|
|
541
|
-
title: 'Account Type'
|
|
542
|
-
},
|
|
543
|
-
firstName: { type: 'string', title: 'First Name' },
|
|
544
|
-
lastName: { type: 'string', title: 'Last Name' },
|
|
545
|
-
businessName: { type: 'string', title: 'Business Name' },
|
|
546
|
-
taxId: { type: 'string', title: 'Tax ID' }
|
|
547
|
-
}
|
|
819
|
+
// Complete 5-step wizard with dynamic conditionals
|
|
820
|
+
const companyOnboardingSchema = {
|
|
821
|
+
STEP_COMPANY: { /* Company details */ },
|
|
822
|
+
STEP_DIRECTORS: { /* Dynamic director array */ },
|
|
823
|
+
STEP_ADDRESS: { /* Address with postal logic */ },
|
|
824
|
+
STEP_FINANCIAL: { /* Banking with international support */ },
|
|
825
|
+
STEP_COMPLIANCE: { /* Regulatory requirements */ }
|
|
548
826
|
};
|
|
549
827
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
},
|
|
560
|
-
{
|
|
561
|
-
conditions: {
|
|
562
|
-
accountType: { equals: 'business' }
|
|
563
|
-
},
|
|
564
|
-
event: {
|
|
565
|
-
type: 'show',
|
|
566
|
-
params: { fields: ['businessName', 'taxId'] }
|
|
828
|
+
// Per-director conditionals with O(N) performance
|
|
829
|
+
const uiSchema = {
|
|
830
|
+
STEP_DIRECTORS: {
|
|
831
|
+
directors: {
|
|
832
|
+
items: createDynamicArrayItemsUiSchema((director) => ({
|
|
833
|
+
tfn: { 'ui:widget': director.isAustralianResident ? 'text' : 'hidden' },
|
|
834
|
+
passport: { 'ui:widget': !director.isAustralianResident ? 'text' : 'hidden' }
|
|
835
|
+
}))
|
|
567
836
|
}
|
|
568
837
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
<ConditionalForm
|
|
572
|
-
schema={schema}
|
|
573
|
-
conditionalRules={conditionalRules}
|
|
574
|
-
onSubmit={handleSubmit}
|
|
575
|
-
/>
|
|
838
|
+
};
|
|
576
839
|
```
|
|
577
840
|
|
|
578
|
-
###
|
|
841
|
+
### 💼 Multi-Step Workflows
|
|
579
842
|
|
|
843
|
+
**Loan Applications**
|
|
844
|
+
- Conditional document requirements based on loan type
|
|
845
|
+
- Income verification with employment status logic
|
|
846
|
+
- Asset declaration with property type conditionals
|
|
847
|
+
|
|
848
|
+
**Employee Onboarding**
|
|
849
|
+
- Role-based form sections (technical vs. non-technical)
|
|
850
|
+
- Security clearance workflows
|
|
851
|
+
- Benefits enrollment with eligibility rules
|
|
852
|
+
|
|
853
|
+
**Grant Applications**
|
|
854
|
+
- Multi-stage submissions with validation gates
|
|
855
|
+
- Budget calculations with category-based rules
|
|
856
|
+
- Compliance checks based on funding type
|
|
857
|
+
|
|
858
|
+
### 🔄 Dynamic Array Management
|
|
859
|
+
|
|
860
|
+
**Board of Directors Management**
|
|
580
861
|
```tsx
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
}
|
|
594
|
-
},
|
|
595
|
-
{
|
|
596
|
-
// Show verification for high income personal OR business accounts
|
|
597
|
-
conditions: {
|
|
598
|
-
any: [
|
|
599
|
-
{
|
|
600
|
-
all: [
|
|
601
|
-
{ accountType: { equals: 'personal' } },
|
|
602
|
-
{ income: { greaterThan: 100000 } }
|
|
603
|
-
]
|
|
862
|
+
// Professional-grade array handling with O(N) performance
|
|
863
|
+
import { createDynamicArrayItemsUiSchema } from 'extended-dynamic-forms';
|
|
864
|
+
|
|
865
|
+
const uiSchema = {
|
|
866
|
+
STEP_DIRECTORS: {
|
|
867
|
+
directors: {
|
|
868
|
+
items: createDynamicArrayItemsUiSchema((director, index) => ({
|
|
869
|
+
shareholdingPercentage: {
|
|
870
|
+
'ui:widget': director.hasShareholding ? 'percentage' : 'hidden'
|
|
871
|
+
},
|
|
872
|
+
tfn: {
|
|
873
|
+
'ui:widget': director.isAustralianResident ? 'text' : 'hidden'
|
|
604
874
|
},
|
|
605
|
-
{
|
|
606
|
-
|
|
875
|
+
passport: {
|
|
876
|
+
'ui:widget': !director.isAustralianResident ? 'text' : 'hidden'
|
|
607
877
|
}
|
|
608
|
-
|
|
609
|
-
},
|
|
610
|
-
event: {
|
|
611
|
-
type: 'require',
|
|
612
|
-
params: { fields: ['verificationDocument'] }
|
|
878
|
+
}))
|
|
613
879
|
}
|
|
614
880
|
}
|
|
615
|
-
|
|
881
|
+
};
|
|
616
882
|
```
|
|
617
883
|
|
|
618
|
-
|
|
884
|
+
**Product Configurations**
|
|
885
|
+
- Dynamic product options based on category
|
|
886
|
+
- Pricing calculations with quantity breaks
|
|
887
|
+
- Feature availability by subscription tier
|
|
619
888
|
|
|
620
|
-
|
|
621
|
-
-
|
|
622
|
-
-
|
|
623
|
-
-
|
|
624
|
-
- `in` / `notIn` - Value in array
|
|
625
|
-
- `empty` / `notEmpty` - Check if field has value
|
|
889
|
+
**Address Management**
|
|
890
|
+
- International vs. domestic address formats
|
|
891
|
+
- State/province selection based on country
|
|
892
|
+
- Postal code validation by region
|
|
626
893
|
|
|
627
|
-
###
|
|
894
|
+
### 🌐 CMS & Integration Use Cases
|
|
628
895
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
url: 'https://api.example.com/validate',
|
|
642
|
-
events: ['change'],
|
|
643
|
-
debounceMs: 500
|
|
896
|
+
**WordPress/Drupal Forms**
|
|
897
|
+
```html
|
|
898
|
+
<!-- Standalone JavaScript integration -->
|
|
899
|
+
<script>
|
|
900
|
+
const { createWizard } = ExtendedDynamicForms;
|
|
901
|
+
|
|
902
|
+
createWizard({
|
|
903
|
+
schema: wpFormSchema,
|
|
904
|
+
container: '#wp-form-container',
|
|
905
|
+
onSubmit: (data) => {
|
|
906
|
+
// Submit to WordPress REST API
|
|
907
|
+
wp.apiRequest({ path: '/wp/v2/forms', method: 'POST', data });
|
|
644
908
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
// Local handling alongside conditional logic
|
|
648
|
-
console.log('Field changed:', event);
|
|
649
|
-
}}
|
|
650
|
-
/>
|
|
909
|
+
});
|
|
910
|
+
</script>
|
|
651
911
|
```
|
|
652
912
|
|
|
653
|
-
|
|
913
|
+
**Legacy System Modernization**
|
|
914
|
+
- Drop-in replacement for static HTML forms
|
|
915
|
+
- Progressive enhancement of existing forms
|
|
916
|
+
- No-build deployment to production environments
|
|
654
917
|
|
|
655
|
-
|
|
918
|
+
## Architecture
|
|
656
919
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
// Webhook configuration
|
|
672
|
-
webhooks?: WebhookConfig[];
|
|
673
|
-
|
|
674
|
-
// Form lifecycle events
|
|
675
|
-
beforeSubmit?: (data: any) => Promise<any> | any;
|
|
676
|
-
afterSubmit?: (data: any) => Promise<void> | void;
|
|
677
|
-
beforeValidation?: (data: any) => Promise<any> | any;
|
|
678
|
-
afterValidation?: (data: any, errors: any) => Promise<void> | void;
|
|
679
|
-
}
|
|
920
|
+
Extended Dynamic Forms uses a layered architecture that cleanly extends RJSF:
|
|
921
|
+
|
|
922
|
+
```
|
|
923
|
+
┌─────────────────────────────────────────────────┐
|
|
924
|
+
│ Three Pillars v2 (Conditional System) │
|
|
925
|
+
├─────────────────────────────────────────────────┤
|
|
926
|
+
│ Application Layer (WizardForm) │
|
|
927
|
+
├─────────────────────────────────────────────────┤
|
|
928
|
+
│ Event Layer (FormEventHub) │
|
|
929
|
+
├─────────────────────────────────────────────────┤
|
|
930
|
+
│ Component Layer (Widgets/Fields/Templates) │
|
|
931
|
+
├─────────────────────────────────────────────────┤
|
|
932
|
+
│ Base Layer (@rjsf/antd) │
|
|
933
|
+
└─────────────────────────────────────────────────┘
|
|
680
934
|
```
|
|
681
935
|
|
|
682
|
-
|
|
936
|
+
## Feature Comparison
|
|
937
|
+
|
|
938
|
+
| Feature | Basic RJSF | Extended Dynamic Forms |
|
|
939
|
+
|---------|------------|----------------------|
|
|
940
|
+
| **Form Generation** | ✅ JSON Schema | ✅ Enhanced JSON Schema |
|
|
941
|
+
| **UI Components** | Basic HTML | ✅ Comprehensive Ant Design Suite |
|
|
942
|
+
| **Conditional Logic** | ❌ Manual | ✅ Three Pillars v2 (O(N)) |
|
|
943
|
+
| **Multi-Step Forms** | ❌ Manual | ✅ Automatic Wizard Detection |
|
|
944
|
+
| **Event System** | Basic onChange | ✅ Central Event Orchestration |
|
|
945
|
+
| **Webhook Integration** | ❌ None | ✅ Production-Ready with Retry |
|
|
946
|
+
| **Array Conditionals** | ❌ None | ✅ Per-Item O(N) Performance |
|
|
947
|
+
| **TypeScript** | Partial | ✅ Comprehensive Type Definitions |
|
|
948
|
+
| **Standalone JS** | ❌ React Only | ✅ Complete UMD Bundle |
|
|
949
|
+
| **Testing** | Basic | ✅ Extensive Test Coverage |
|
|
950
|
+
| **Performance** | Standard | ✅ Optimized with Caching |
|
|
951
|
+
| **Validation** | Basic | ✅ Dynamic + Custom Rules |
|
|
952
|
+
| **Developer Tools** | Limited | ✅ Interactive Playground |
|
|
683
953
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
954
|
+
## Development
|
|
955
|
+
|
|
956
|
+
### 🚀 Quick Start
|
|
957
|
+
|
|
958
|
+
```bash
|
|
959
|
+
# Clone and setup
|
|
960
|
+
git clone https://github.com/user/extended-dynamic-forms.git
|
|
961
|
+
cd extended-dynamic-forms
|
|
962
|
+
npm install
|
|
963
|
+
|
|
964
|
+
# Start development with playground
|
|
965
|
+
npm run dev # Launches interactive playground
|
|
966
|
+
npm run playground # Alternative playground command
|
|
967
|
+
|
|
968
|
+
# Development workflow
|
|
969
|
+
npm run test # Run complete test suite
|
|
970
|
+
npm run test:ui # Visual test runner
|
|
971
|
+
npm run lint # ESLint validation
|
|
972
|
+
npm run format # Prettier formatting
|
|
694
973
|
```
|
|
695
974
|
|
|
696
|
-
###
|
|
975
|
+
### 📦 Build Commands
|
|
697
976
|
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}
|
|
977
|
+
```bash
|
|
978
|
+
# Library builds
|
|
979
|
+
npm run build # TypeScript + Vite library build
|
|
980
|
+
npm run build:vanilla # Standalone UMD bundle
|
|
981
|
+
npm run build:all # Both library and standalone
|
|
982
|
+
|
|
983
|
+
# Production validation
|
|
984
|
+
npm run preview # Preview library build
|
|
985
|
+
npm run serve:examples # Serve vanilla JS examples
|
|
708
986
|
```
|
|
709
987
|
|
|
710
|
-
###
|
|
988
|
+
### 🧪 Testing
|
|
711
989
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
any?: Array<{ [fieldPath: string]: FieldCondition }>;
|
|
718
|
-
};
|
|
719
|
-
event: {
|
|
720
|
-
type: 'show' | 'hide' | 'require' | 'unrequire' | 'disable' | 'enable';
|
|
721
|
-
params: {
|
|
722
|
-
fields: string[];
|
|
723
|
-
};
|
|
724
|
-
};
|
|
725
|
-
}
|
|
990
|
+
**Test Coverage**: Extensive test suite with focus on:
|
|
991
|
+
- **Conditional Logic**: Comprehensive coverage for v1 and v2 systems
|
|
992
|
+
- **Wizard Forms**: Step navigation and validation
|
|
993
|
+
- **Performance**: Array conditional optimization
|
|
994
|
+
- **Integration**: Component interaction testing
|
|
726
995
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
greaterThanOrEqual?: number;
|
|
733
|
-
lessThanOrEqual?: number;
|
|
734
|
-
contains?: string;
|
|
735
|
-
notContains?: string;
|
|
736
|
-
in?: any[];
|
|
737
|
-
notIn?: any[];
|
|
738
|
-
empty?: boolean;
|
|
739
|
-
notEmpty?: boolean;
|
|
740
|
-
}
|
|
996
|
+
```bash
|
|
997
|
+
npm run test # Run complete test suite
|
|
998
|
+
npm run test -- --grep "conditional" # Test conditional logic
|
|
999
|
+
npm run test -- --grep "wizard" # Test wizard forms
|
|
1000
|
+
npm run test -- --grep "array" # Test array handling
|
|
741
1001
|
```
|
|
742
1002
|
|
|
743
|
-
|
|
1003
|
+
### 🏗️ Architecture
|
|
744
1004
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
const isEmail = validationUtils.isValidEmail('test@example.com');
|
|
764
|
-
const isPhone = validationUtils.isValidPhone('(555) 123-4567');
|
|
765
|
-
const isUrl = validationUtils.isValidUrl('https://example.com');
|
|
766
|
-
const isDate = validationUtils.isValidDate('2024-01-01');
|
|
767
|
-
const inRange = validationUtils.isInRange(5, 1, 10);
|
|
768
|
-
const message = validationUtils.getValidationMessage('required');
|
|
1005
|
+
**Build Configuration**:
|
|
1006
|
+
- **Library**: ES + UMD via Vite with TypeScript declarations
|
|
1007
|
+
- **Standalone**: Complete UMD bundle with React/antd included
|
|
1008
|
+
- **External Dependencies**: React, antd marked as externals in library build
|
|
1009
|
+
- **Type Generation**: `vite-plugin-dts` for .d.ts files
|
|
1010
|
+
|
|
1011
|
+
**File Structure**:
|
|
1012
|
+
```
|
|
1013
|
+
src/
|
|
1014
|
+
├── ExtendedForm.tsx # Main form wrapper
|
|
1015
|
+
├── WizardForm.tsx # Multi-step orchestrator
|
|
1016
|
+
├── conditionals/ # v1 + v2 conditional systems
|
|
1017
|
+
│ ├── v2/ # Three Pillars architecture
|
|
1018
|
+
│ └── DynamicArrayConditionalEngine.ts
|
|
1019
|
+
├── events/ # Central event orchestration
|
|
1020
|
+
├── widgets/ # Enhanced component suite
|
|
1021
|
+
├── components/ # Field and template components
|
|
1022
|
+
└── utils/ # Form manipulation utilities
|
|
769
1023
|
```
|
|
770
1024
|
|
|
771
|
-
##
|
|
1025
|
+
## Examples & Demos
|
|
772
1026
|
|
|
773
|
-
|
|
774
|
-
# Install dependencies
|
|
775
|
-
npm install
|
|
1027
|
+
### 🎮 Interactive Playground
|
|
776
1028
|
|
|
777
|
-
|
|
1029
|
+
The library includes a comprehensive **interactive playground** with extensive real-world demos demonstrating all features:
|
|
1030
|
+
|
|
1031
|
+
```bash
|
|
1032
|
+
# Run the playground locally
|
|
778
1033
|
npm run dev
|
|
1034
|
+
# or
|
|
1035
|
+
npm run playground
|
|
1036
|
+
```
|
|
779
1037
|
|
|
780
|
-
|
|
781
|
-
|
|
1038
|
+
### 📚 Demo Categories
|
|
1039
|
+
|
|
1040
|
+
#### **Three Pillars v2 Demonstrations**
|
|
1041
|
+
- **Global UI Rules**: Dynamic show/hide with JSON Patch operations
|
|
1042
|
+
- **Dynamic Array Items**: Per-item conditional logic with O(N) performance
|
|
1043
|
+
- **Conditional Validation**: Custom validation rules without schema changes
|
|
1044
|
+
- **Migration Examples**: Automated v1-to-v2 rule conversion
|
|
1045
|
+
|
|
1046
|
+
#### **🧙♂️ Wizard Form Showcases**
|
|
1047
|
+
- **Australian Company Onboarding**: 5-step enterprise form with:
|
|
1048
|
+
- Dynamic director arrays with per-item conditionals
|
|
1049
|
+
- Real-time fee calculations based on business rules
|
|
1050
|
+
- Complex validation (ABN/ACN numbers, postcodes)
|
|
1051
|
+
- Step-aware conditional logic
|
|
1052
|
+
- Professional business workflow
|
|
1053
|
+
|
|
1054
|
+
#### **🔗 Event System Demos**
|
|
1055
|
+
- **Real-time Form Analytics**: Webhook integration with retry logic
|
|
1056
|
+
- **Field-Level Event Handling**: Focus/blur/change event orchestration
|
|
1057
|
+
- **Webhook Configuration**: Multiple endpoints with custom headers
|
|
1058
|
+
|
|
1059
|
+
#### **🎨 Widget Showcase**
|
|
1060
|
+
- **Enhanced Standard Widgets**: All RJSF widgets with Ant Design styling
|
|
1061
|
+
- **Custom Widgets**: Color picker, rating, range slider, file upload
|
|
1062
|
+
- **Professional Fields**: Address, currency, phone number components
|
|
1063
|
+
|
|
1064
|
+
### 📱 Vanilla JavaScript Examples
|
|
1065
|
+
|
|
1066
|
+
Complete standalone examples in `/examples/vanilla-js/`:
|
|
1067
|
+
|
|
1068
|
+
```html
|
|
1069
|
+
<!-- Basic Form -->
|
|
1070
|
+
basic-form.html <!-- Simple contact form -->
|
|
1071
|
+
conditional-form.html <!-- Dynamic field visibility -->
|
|
1072
|
+
wizard-form.html <!-- Multi-step onboarding -->
|
|
1073
|
+
```
|
|
782
1074
|
|
|
783
|
-
|
|
784
|
-
npm test
|
|
1075
|
+
### 🔗 Live Demo Links
|
|
785
1076
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1077
|
+
- **[Playground](https://extended-dynamic-forms-playground.vercel.app)** - Interactive demo environment
|
|
1078
|
+
- **[Australian Company Onboarding](https://extended-dynamic-forms-playground.vercel.app/wizard/australian-company)** - Full enterprise form showcase
|
|
1079
|
+
- **[Three Pillars v2 Demo](https://extended-dynamic-forms-playground.vercel.app/conditionals/three-pillars)** - Modern conditional logic patterns
|
|
1080
|
+
|
|
1081
|
+
## Contributing
|
|
1082
|
+
|
|
1083
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
1084
|
+
|
|
1085
|
+
## Community & Support
|
|
1086
|
+
|
|
1087
|
+
- 📚 [Documentation](https://extended-dynamic-forms.dev)
|
|
1088
|
+
- 🐛 [Issue Tracker](https://github.com/user/extended-dynamic-forms/issues)
|
|
1089
|
+
- 💬 [Discussions](https://github.com/user/extended-dynamic-forms/discussions)
|
|
1090
|
+
- 📧 [Email Support](mailto:support@extended-dynamic-forms.dev)
|
|
789
1091
|
|
|
790
1092
|
## License
|
|
791
1093
|
|
|
792
|
-
MIT
|
|
1094
|
+
MIT © [Your Name/Organization]
|
|
1095
|
+
|
|
1096
|
+
---
|
|
1097
|
+
|
|
1098
|
+
<p align="center">
|
|
1099
|
+
Built with ❤️ on top of <a href="https://github.com/rjsf-team/react-jsonschema-form">React JSON Schema Form</a>
|
|
1100
|
+
</p>
|