extended-dynamic-forms 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +398 -1075
- package/dist/WizardForm.d.ts +4 -1
- package/dist/events/FormEventContext.d.ts +3 -0
- package/dist/events/FormEventHub.d.ts +19 -2
- package/dist/index.es.js +21051 -12923
- package/dist/index.umd.js +63 -36
- package/dist/style.css +1 -1
- package/dist/types/index.d.ts +6 -1
- package/dist/utils/fieldChangeDetector.d.ts +22 -0
- package/dist/widgets/base/index.d.ts +2 -0
- package/dist/widgets/base/types.d.ts +135 -0
- package/dist/widgets/base/utils.d.ts +63 -0
- package/dist/widgets/choice/ChoiceWidget.d.ts +30 -0
- package/dist/widgets/choice/ChoiceWidget.types.d.ts +496 -0
- package/dist/widgets/choice/examples/ChoiceWidget.example.d.ts +7 -0
- package/dist/widgets/choice/hooks/__tests__/useChoiceWidget.test.d.ts +1 -0
- package/dist/widgets/choice/hooks/__tests__/useDataSource.test.d.ts +1 -0
- package/dist/widgets/choice/hooks/examples/useChoiceWidget.example.d.ts +18 -0
- package/dist/widgets/choice/hooks/examples/useDataSourceExample.d.ts +22 -0
- package/dist/widgets/choice/hooks/index.d.ts +6 -0
- package/dist/widgets/choice/hooks/useChoiceWidget.d.ts +65 -0
- package/dist/widgets/choice/hooks/useDataSource.d.ts +69 -0
- package/dist/widgets/choice/index.d.ts +59 -0
- package/dist/widgets/choice/presenters/CheckboxPresenter.d.ts +70 -0
- package/dist/widgets/choice/presenters/DropdownPresenter.d.ts +37 -0
- package/dist/widgets/choice/presenters/PresenterStrategy.d.ts +230 -0
- package/dist/widgets/choice/presenters/RadioPresenter.d.ts +39 -0
- package/dist/widgets/choice/presenters/examples/RadioPresenterExample.d.ts +7 -0
- package/dist/widgets/choice/presenters/index.d.ts +20 -0
- package/dist/widgets/choice/providers/ApiDataSource.d.ts +241 -0
- package/dist/widgets/choice/providers/DataSourceProvider.d.ts +260 -0
- package/dist/widgets/choice/providers/StaticDataSource.d.ts +258 -0
- package/dist/widgets/choice/providers/WebSocketDataSource.d.ts +310 -0
- package/dist/widgets/choice/providers/index.d.ts +12 -0
- package/dist/widgets/choice/utils/index.d.ts +1 -0
- package/dist/widgets/choice/utils/responseMappers.d.ts +342 -0
- package/dist/widgets/date/DateTimeWidget.d.ts +26 -0
- package/dist/widgets/date/EnhancedDateWidget.d.ts +26 -0
- package/dist/widgets/date/index.d.ts +13 -0
- package/dist/widgets/file/FileUploadWidget.d.ts +40 -0
- package/dist/widgets/file/index.d.ts +2 -0
- package/dist/widgets/index.d.ts +33 -17
- package/dist/widgets/input/EmailWidget.d.ts +34 -0
- package/dist/widgets/input/NumberWidget.d.ts +39 -0
- package/dist/widgets/input/TelephoneWidget.d.ts +34 -0
- package/dist/widgets/input/URLWidget.d.ts +34 -0
- package/dist/widgets/input/index.d.ts +7 -0
- package/dist/widgets/text/PasswordWidget.d.ts +45 -0
- package/dist/widgets/text/TextWidget.d.ts +40 -0
- package/dist/widgets/text/TextareaWidget.d.ts +38 -0
- package/dist/widgets/text/index.d.ts +20 -0
- package/package.json +8 -8
- package/dist/vanilla-entry.d.ts +0 -204
- package/dist/widgets/enhanced/EnhancedWidgets.d.ts +0 -37
- package/examples/conditional-v2-example.tsx +0 -64
- package/examples/dynamic-array-items-example.tsx +0 -211
- package/examples/form-devtools-example.tsx +0 -384
- package/examples/global-ui-rules-example.tsx +0 -371
- package/examples/vanilla-js/README.md +0 -212
- package/examples/vanilla-js/basic-form.html +0 -142
- package/examples/vanilla-js/conditional-form.html +0 -159
- package/examples/vanilla-js/wizard-form.html +0 -185
package/README.md
CHANGED
|
@@ -1,1220 +1,543 @@
|
|
|
1
1
|
# Extended Dynamic Forms
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## BREAKING CHANGE: v0.2.0
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
>
|
|
7
|
-
> **Migration Required**: If you are using function-based conditions like `condition: (formData) => ...`, you must migrate to JsonLogic format: `condition: { "==": [{ "var": "fieldName" }, "value"] }`
|
|
5
|
+
All conditional logic now uses **JsonLogic rules only** for conditions. Function-based conditions have been removed for security and portability. Note that validation effects may still use functions. See migration examples below.
|
|
8
6
|
|
|
9
|
-
##
|
|
7
|
+
## Project Status
|
|
10
8
|
|
|
11
|
-
**Important
|
|
9
|
+
**Important Notes:**
|
|
10
|
+
- Requires RJSF source in sibling directory for development
|
|
11
|
+
- Cannot be installed from npm due to local file dependencies
|
|
12
|
+
- JSON logic conditions don't work with vanilla JavaScript builds
|
|
13
|
+
- Array drag-and-drop is not implemented
|
|
14
|
+
- Limited test coverage for UI components and event system
|
|
15
|
+
- JSON Patch implementation supports add/remove/replace only (not move/copy/test)
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
- The project currently uses local file dependencies that require a specific directory structure to build
|
|
15
|
-
- The project requires RJSF packages from a sibling directory (../react-jsonschema-form/)
|
|
16
|
-
- Some playground demos use deprecated v1 conditional patterns (01-simple-conditionals.demo.tsx)
|
|
17
|
-
- Array drag-and-drop functionality mentioned in documentation is not implemented
|
|
18
|
-
- Test coverage is limited for UI components and event systems
|
|
19
|
-
- Several documented features have incomplete implementations
|
|
20
|
-
- Significant dead code: 76% of utility functions are unused (13 out of 17 functions)
|
|
21
|
-
- JSON Patch implementation in v2 doesn't fully comply with RFC 6902 (no move/copy/test operations)
|
|
17
|
+
## Overview
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
Extended Dynamic Forms is an extension library for React JSON Schema Form (RJSF) v6 that adds:
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
[](https://www.typescriptlang.org/)
|
|
31
|
-
[](#standalone-javascript-support)
|
|
32
|
-
|
|
33
|
-
## Why Extended Dynamic Forms?
|
|
34
|
-
|
|
35
|
-
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:
|
|
36
|
-
|
|
37
|
-
### 🚀 **Performance Revolution**
|
|
38
|
-
- **O(N) Conditional Logic**: Advanced Three Pillars v2 architecture vs traditional O(M×N) systems
|
|
39
|
-
- **Optimized Array Processing**: Per-item evaluation with dynamic array conditionals
|
|
40
|
-
- **Memory Efficient**: Immutable schema processing with intelligent caching
|
|
41
|
-
|
|
42
|
-
### 🎯 **Enterprise-Grade Features**
|
|
43
|
-
- **Complex Conditional Logic**: Multi-field dependencies, nested conditions, and dynamic validation
|
|
44
|
-
- **Multi-Step Workflows**: Automatic wizard detection with step-aware conditional logic
|
|
45
|
-
- **Real-Time Integration**: Production-ready webhooks with retry logic and error isolation
|
|
46
|
-
- **Professional UI**: Comprehensive suite of enhanced Ant Design components with consistent styling
|
|
47
|
-
|
|
48
|
-
### 🔄 **Developer Experience**
|
|
49
|
-
- **Dual Architecture Support**: Modern React hooks + vanilla JavaScript compatibility
|
|
50
|
-
- **Type-Safe Throughout**: Comprehensive TypeScript interfaces and validation
|
|
51
|
-
- **Rich Ecosystem**: Interactive playground with comprehensive real-world demos
|
|
52
|
-
- **Migration Ready**: Automated v1-to-v2 conversion utilities
|
|
53
|
-
|
|
54
|
-
This library transforms RJSF from a simple form generator into a complete enterprise form solution while preserving its declarative approach.
|
|
55
|
-
|
|
56
|
-
## Core Features
|
|
57
|
-
|
|
58
|
-
### 🚀 Three Pillars v2 Architecture with JsonLogic
|
|
59
|
-
- **UI Logic Pillar**: Control form appearance with JsonLogic conditions and JSON Patch operations
|
|
60
|
-
- **Schema Logic Pillar**: Modify data structure with JsonLogic rules when truly needed
|
|
61
|
-
- **Validation Logic Pillar**: Dynamic validation rules with JsonLogic conditions
|
|
62
|
-
- **O(N) Performance**: Superior array handling vs v1's O(M×N)
|
|
63
|
-
- **JsonLogic-Only**: Secure, data-driven conditions without code execution
|
|
64
|
-
- **React Hooks**: Modern, declarative API with `useConditionalUi`, `useConditionalSchema`, `useConditionalValidation`
|
|
65
|
-
|
|
66
|
-
### 🎯 Declarative Conditional Logic with JsonLogic
|
|
67
|
-
- Define complex form behavior with secure JsonLogic rules
|
|
68
|
-
- Dynamic array support with per-item evaluation
|
|
69
|
-
- No code execution - all conditions are data-driven
|
|
70
|
-
- Type-safe with comprehensive TypeScript support
|
|
71
|
-
|
|
72
|
-
```tsx
|
|
73
|
-
import { UiRule, JsonLogicRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
74
|
-
|
|
75
|
-
const uiRules: UiRule[] = [{
|
|
76
|
-
name: 'Show premium features for premium users',
|
|
77
|
-
condition: {
|
|
78
|
-
"==": [{ "var": "accountType" }, "premium"]
|
|
79
|
-
} as JsonLogicRule,
|
|
80
|
-
effect: [
|
|
81
|
-
{ op: 'remove', path: '/premiumFeatures/ui:widget' }
|
|
82
|
-
]
|
|
83
|
-
}];
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 🧙♂️ Multi-Step Wizard Forms
|
|
87
|
-
- Automatic step detection from schema structure
|
|
88
|
-
- **Full conditional logic support** with step-aware rule processing
|
|
89
|
-
- Built-in progress indicators and navigation
|
|
90
|
-
- Per-step validation
|
|
91
|
-
- Supports both v1 conditions (deprecated) and v2 Three Pillars architecture
|
|
92
|
-
|
|
93
|
-
**Step-Aware Conditional Logic**
|
|
94
|
-
|
|
95
|
-
The `WizardForm` automatically filters and transforms conditional rules to work within the current step's context. This means you can define conditions that reference fields across different steps, and the wizard will intelligently apply only the relevant rules for the active step:
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
// Define steps with prefixed field names to avoid collisions
|
|
99
|
-
const schema = {
|
|
100
|
-
type: 'object',
|
|
101
|
-
properties: {
|
|
102
|
-
STEP_PERSONAL: {
|
|
103
|
-
type: 'object',
|
|
104
|
-
title: 'Personal Information',
|
|
105
|
-
properties: {
|
|
106
|
-
isUSCitizen: { type: 'boolean', title: 'Are you a US Citizen?' },
|
|
107
|
-
ssn: { type: 'string', title: 'Social Security Number' }
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// ... other steps
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// Use v2 UiRules with JsonLogic to handle step-aware conditionals
|
|
115
|
-
const uiRules: UiRule[] = [{
|
|
116
|
-
name: 'Show SSN only for US Citizens in Personal step',
|
|
117
|
-
condition: {
|
|
118
|
-
"!": { "var": "STEP_PERSONAL.isUSCitizen" }
|
|
119
|
-
} as JsonLogicRule,
|
|
120
|
-
effect: [
|
|
121
|
-
{ op: 'add', path: '/STEP_PERSONAL/properties/ssn/ui:widget', value: 'hidden' }
|
|
122
|
-
]
|
|
123
|
-
}];
|
|
124
|
-
|
|
125
|
-
// The WizardForm handles the step isolation automatically
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### 🔗 Central Event Orchestration
|
|
129
|
-
- Unified event system for all field interactions
|
|
130
|
-
- Webhook integration with retry logic
|
|
131
|
-
- Real-time form analytics capabilities
|
|
132
|
-
- Debounced event processing
|
|
133
|
-
|
|
134
|
-
### 🎨 Ant Design Components
|
|
135
|
-
- Complete widget suite built with Ant Design v5
|
|
136
|
-
- Enhanced standard widgets with event integration
|
|
137
|
-
- Custom widgets: Rating, Color Picker, Range Slider
|
|
138
|
-
- Professional field components: Address, Phone, Currency
|
|
139
|
-
|
|
140
|
-
### ⚡ Form Lifecycle Hooks
|
|
141
|
-
- Intercept and modify form data at key stages
|
|
142
|
-
- `beforeSubmit`, `afterSubmit`, `beforeValidation`, `afterValidation`
|
|
143
|
-
- Ideal for data transformation, logging, or triggering side effects
|
|
144
|
-
|
|
145
|
-
### 📦 Additional Features
|
|
146
|
-
- **TypeScript**: Full type safety and IntelliSense with comprehensive interface definitions
|
|
147
|
-
- **Performance**: Optimized re-renders and memoization with intelligent caching
|
|
148
|
-
- **Extensible**: Add custom widgets, fields, and actions with clean APIs
|
|
149
|
-
- **Test Coverage**: Focused on conditional logic and wizard forms (UI components have limited coverage)
|
|
150
|
-
- **Developer Experience**: Interactive playground with real-world demos (note: some deprecated)
|
|
151
|
-
|
|
152
|
-
### 🌐 Standalone JavaScript Support
|
|
153
|
-
|
|
154
|
-
**Complete vanilla JavaScript compatibility** with a production-ready UMD bundle that includes React, Ant Design, and all dependencies:
|
|
155
|
-
|
|
156
|
-
```html
|
|
157
|
-
<!DOCTYPE html>
|
|
158
|
-
<html>
|
|
159
|
-
<head>
|
|
160
|
-
<script src="https://unpkg.com/extended-dynamic-forms/dist/standalone.umd.js"></script>
|
|
161
|
-
<link rel="stylesheet" href="https://unpkg.com/extended-dynamic-forms/dist/style.css">
|
|
162
|
-
</head>
|
|
163
|
-
<body>
|
|
164
|
-
<div id="form-container"></div>
|
|
165
|
-
|
|
166
|
-
<script>
|
|
167
|
-
const { createForm, createWizard } = ExtendedDynamicForms;
|
|
168
|
-
|
|
169
|
-
// Create a complete form without React knowledge
|
|
170
|
-
const form = createForm({
|
|
171
|
-
schema: {
|
|
172
|
-
type: 'object',
|
|
173
|
-
properties: {
|
|
174
|
-
name: { type: 'string', title: 'Full Name' },
|
|
175
|
-
email: { type: 'string', format: 'email', title: 'Email' }
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
container: document.getElementById('form-container'),
|
|
179
|
-
onSubmit: (data) => console.log('Form submitted:', data)
|
|
180
|
-
});
|
|
181
|
-
</script>
|
|
182
|
-
</body>
|
|
183
|
-
</html>
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**Important Notes on Conditional Logic in Vanilla JS:**
|
|
187
|
-
- **JsonLogic is NOT supported in vanilla JS** - The JsonLogic runtime requires React hooks
|
|
188
|
-
- Only v1 conditions are supported with the `conditionals` prop
|
|
189
|
-
- Subject to v1's O(M×N) performance for array conditionals
|
|
190
|
-
- For JsonLogic and v2 benefits, you must use the React-based API
|
|
191
|
-
|
|
192
|
-
```javascript
|
|
193
|
-
// Vanilla JS supports only v1 conditionals
|
|
194
|
-
ExtendedDynamicForms.createForm({
|
|
195
|
-
container: '#my-form',
|
|
196
|
-
schema: schema,
|
|
197
|
-
conditionals: {
|
|
198
|
-
rules: [
|
|
199
|
-
{
|
|
200
|
-
conditions: { showField: true },
|
|
201
|
-
event: { type: 'show', params: { field: 'conditionalField' } }
|
|
202
|
-
}
|
|
203
|
-
]
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
**Perfect for:**
|
|
209
|
-
- WordPress/Drupal integrations
|
|
210
|
-
- Legacy applications
|
|
211
|
-
- CMS form builders
|
|
212
|
-
- No-build environments
|
|
213
|
-
- PHP/ASP.NET applications
|
|
214
|
-
|
|
215
|
-
## Credits & Acknowledgments
|
|
216
|
-
|
|
217
|
-
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.
|
|
218
|
-
|
|
219
|
-
Special thanks to the RJSF team and contributors for creating such a flexible and well-architected foundation.
|
|
21
|
+
- **Three Pillars Architecture** for conditional logic with O(N) performance (where N is the number of conditional rules/array items)
|
|
22
|
+
- **Multi-step wizard forms** with automatic step detection
|
|
23
|
+
- **Central event orchestration** with webhook support
|
|
24
|
+
- **25+ Ant Design components** for professional UI
|
|
25
|
+
- **TypeScript-first** development with full type safety
|
|
220
26
|
|
|
221
27
|
## Installation
|
|
222
28
|
|
|
223
|
-
|
|
224
|
-
> - The library cannot be installed directly from npm without modifications
|
|
225
|
-
> - You need the RJSF source code in a sibling directory for development
|
|
226
|
-
> - Consider using a monorepo structure or git submodules for proper dependency management
|
|
227
|
-
|
|
228
|
-
### React Projects
|
|
29
|
+
**Note:** This library currently requires local development setup and cannot be installed via npm due to local file dependencies.
|
|
229
30
|
|
|
230
31
|
```bash
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# or
|
|
235
|
-
pnpm add extended-dynamic-forms
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Standalone JavaScript (No Build Required)
|
|
239
|
-
|
|
240
|
-
```html
|
|
241
|
-
<!-- Production CDN -->
|
|
242
|
-
<script src="https://unpkg.com/extended-dynamic-forms@latest/dist/standalone.umd.js"></script>
|
|
243
|
-
<link rel="stylesheet" href="https://unpkg.com/extended-dynamic-forms@latest/dist/style.css">
|
|
32
|
+
# Clone RJSF as sibling directory (required)
|
|
33
|
+
cd ..
|
|
34
|
+
git clone https://github.com/rjsf-team/react-jsonschema-form.git
|
|
244
35
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
### Peer Dependencies
|
|
250
|
-
|
|
251
|
-
**Current Version**: `0.2.0` - Built for **RJSF v6** with Three Pillars v2 architecture (JsonLogic-only).
|
|
252
|
-
|
|
253
|
-
```json
|
|
254
|
-
{
|
|
255
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
256
|
-
"react-dom": "^18.0.0 || ^19.0.0",
|
|
257
|
-
"antd": "^5.24.0"
|
|
258
|
-
}
|
|
36
|
+
# Setup Extended Dynamic Forms
|
|
37
|
+
cd extended-dynamic-forms
|
|
38
|
+
npm install
|
|
39
|
+
npm run dev
|
|
259
40
|
```
|
|
260
41
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
**RJSF Dependencies** (automatically handled):
|
|
264
|
-
- `@rjsf/core`: ^6.0.0+
|
|
265
|
-
- `@rjsf/utils`: ^6.0.0+
|
|
266
|
-
- `@rjsf/validator-ajv8`: ^6.0.0+
|
|
267
|
-
- `@rjsf/antd`: ^6.0.0+
|
|
42
|
+
**Important:** The library is currently designed for internal use or as part of a monorepo setup. Standalone npm package distribution is not yet supported.
|
|
268
43
|
|
|
269
|
-
|
|
44
|
+
## Core Architecture
|
|
270
45
|
|
|
271
|
-
|
|
272
|
-
- `dayjs`: ^1.11.13 (Date handling)
|
|
273
|
-
- `json-rules-engine-simplified`: ^0.2.0 (v1 conditional logic)
|
|
46
|
+
### Three Pillars Conditional System
|
|
274
47
|
|
|
275
|
-
|
|
48
|
+
The conditional logic system is divided into three independent pillars:
|
|
276
49
|
|
|
277
|
-
|
|
50
|
+
#### 1. UI Logic Pillar
|
|
51
|
+
Controls form appearance using JsonLogic conditions and JSON Patch operations:
|
|
278
52
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
This package uses Ant Design components. Import the CSS in your application:
|
|
53
|
+
```typescript
|
|
54
|
+
import { useConditionalUi, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
282
55
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
56
|
+
const uiRules: UiRule[] = [{
|
|
57
|
+
name: 'Show license number when has license',
|
|
58
|
+
condition: { "==": [{ "var": "hasDriversLicense" }, true] },
|
|
59
|
+
effect: [{ op: 'remove', path: '/licenseNumber/ui:widget' }]
|
|
60
|
+
}];
|
|
287
61
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Since Extended Dynamic Forms uses Ant Design components, you can customize the appearance using Ant Design's powerful theming system:
|
|
291
|
-
|
|
292
|
-
```tsx
|
|
293
|
-
import { ConfigProvider, theme } from 'antd';
|
|
294
|
-
import { ExtendedForm, WizardForm } from 'extended-dynamic-forms';
|
|
295
|
-
|
|
296
|
-
function App() {
|
|
297
|
-
return (
|
|
298
|
-
<ConfigProvider
|
|
299
|
-
theme={{
|
|
300
|
-
token: {
|
|
301
|
-
colorPrimary: '#00b96b',
|
|
302
|
-
borderRadius: 8,
|
|
303
|
-
fontSize: 16,
|
|
304
|
-
},
|
|
305
|
-
algorithm: theme.darkAlgorithm, // Optional: Use dark theme
|
|
306
|
-
}}
|
|
307
|
-
>
|
|
308
|
-
<WizardForm
|
|
309
|
-
schema={schema}
|
|
310
|
-
uiSchema={uiSchema}
|
|
311
|
-
conditionals={conditionals}
|
|
312
|
-
/>
|
|
313
|
-
</ConfigProvider>
|
|
314
|
-
);
|
|
315
|
-
}
|
|
62
|
+
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
316
63
|
```
|
|
317
64
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
> ⚠️ **BREAKING CHANGE**: As of v0.2.0, the Three Pillars v2 system ONLY supports JsonLogic conditions. Function-based conditions are no longer supported for security and maintainability reasons.
|
|
321
|
-
|
|
322
|
-
The Three Pillars v2 system with JsonLogic provides superior performance, cleaner separation of concerns, and better security. It consists of three independent pillars:
|
|
323
|
-
|
|
324
|
-
### 1. UI Logic Pillar (`useConditionalUi`)
|
|
325
|
-
|
|
326
|
-
Controls form appearance using JSON Patch operations with JsonLogic conditions:
|
|
327
|
-
|
|
328
|
-
```tsx
|
|
329
|
-
// Note: v2 conditionals require deep import for optimal tree-shaking
|
|
330
|
-
import { useConditionalUi, UiRule, JsonLogicRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
331
|
-
|
|
332
|
-
// Define UI rules with JsonLogic conditions and JSON Patch operations
|
|
333
|
-
const uiRules: UiRule[] = [
|
|
334
|
-
{
|
|
335
|
-
name: 'Show shipping address when required',
|
|
336
|
-
condition: {
|
|
337
|
-
"==": [{ "var": "requiresShipping" }, true]
|
|
338
|
-
} as JsonLogicRule,
|
|
339
|
-
effect: [{ op: 'remove', path: '/shippingAddress/ui:widget' }],
|
|
340
|
-
order: 1
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
name: 'Dynamic country fields',
|
|
344
|
-
condition: {
|
|
345
|
-
"==": [{ "var": "shippingAddress.country" }, "USA"]
|
|
346
|
-
} as JsonLogicRule,
|
|
347
|
-
effect: [
|
|
348
|
-
{ op: 'remove', path: '/shippingAddress/state/ui:widget' },
|
|
349
|
-
{ op: 'add', path: '/shippingAddress/province/ui:widget', value: 'hidden' }
|
|
350
|
-
],
|
|
351
|
-
order: 2
|
|
352
|
-
}
|
|
353
|
-
];
|
|
354
|
-
|
|
355
|
-
const baseUiSchema = {
|
|
356
|
-
shippingAddress: {
|
|
357
|
-
'ui:widget': 'hidden', // Initially hidden
|
|
358
|
-
state: { 'ui:widget': 'text' },
|
|
359
|
-
province: { 'ui:widget': 'hidden' } // Initially hidden
|
|
360
|
-
}
|
|
361
|
-
};
|
|
65
|
+
#### 2. Schema Logic Pillar
|
|
66
|
+
Modifies JSON Schema structure dynamically (use sparingly):
|
|
362
67
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
return (
|
|
371
|
-
<ExtendedForm
|
|
372
|
-
schema={schema}
|
|
373
|
-
uiSchema={conditionalUiSchema}
|
|
374
|
-
formData={formData}
|
|
375
|
-
onChange={({ formData }) => setFormData(formData)}
|
|
376
|
-
/>
|
|
377
|
-
);
|
|
378
|
-
}
|
|
68
|
+
```typescript
|
|
69
|
+
const schemaRules: SchemaRule[] = [{
|
|
70
|
+
name: 'Add premium fields',
|
|
71
|
+
condition: { "==": [{ "var": "accountType" }, "premium"] },
|
|
72
|
+
effect: [{ op: 'add', path: '/properties/premiumFeatures', value: {...} }]
|
|
73
|
+
}];
|
|
379
74
|
```
|
|
380
75
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
Modifies JSON Schema structure (use sparingly - prefer UI or Validation pillars):
|
|
384
|
-
|
|
385
|
-
```tsx
|
|
386
|
-
import { useConditionalSchema, SchemaRule, JsonLogicRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
387
|
-
|
|
388
|
-
const schemaRules: SchemaRule[] = [
|
|
389
|
-
{
|
|
390
|
-
name: 'Add premium fields',
|
|
391
|
-
condition: {
|
|
392
|
-
"==": [{ "var": "accountType" }, "premium"]
|
|
393
|
-
} as JsonLogicRule,
|
|
394
|
-
effect: [
|
|
395
|
-
{
|
|
396
|
-
op: 'add',
|
|
397
|
-
path: '/properties/premiumFeatures',
|
|
398
|
-
value: {
|
|
399
|
-
type: 'object',
|
|
400
|
-
properties: {
|
|
401
|
-
priority: { type: 'string', enum: ['high', 'medium', 'low'] }
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
]
|
|
406
|
-
}
|
|
407
|
-
];
|
|
76
|
+
#### 3. Validation Logic Pillar
|
|
77
|
+
Adds conditional validation rules:
|
|
408
78
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const validationRules: ValidationRule[] = [
|
|
421
|
-
{
|
|
422
|
-
name: 'Require consent for minors',
|
|
423
|
-
condition: {
|
|
424
|
-
"<": [{ "var": "age" }, 18]
|
|
425
|
-
} as JsonLogicRule,
|
|
426
|
-
effect: {
|
|
427
|
-
type: 'validation',
|
|
428
|
-
validate: (errors, formData) => {
|
|
429
|
-
if (!formData.parentConsent) {
|
|
430
|
-
errors.parentConsent.addError('Parent consent is required for minors');
|
|
431
|
-
}
|
|
79
|
+
```typescript
|
|
80
|
+
const validationRules: ValidationRule[] = [{
|
|
81
|
+
name: 'Require consent for minors',
|
|
82
|
+
condition: { "<": [{ "var": "age" }, 18] },
|
|
83
|
+
effect: {
|
|
84
|
+
name: 'Parent consent validation',
|
|
85
|
+
isActive: { "<": [{ "var": "age" }, 18] },
|
|
86
|
+
validate: (errors, formData) => {
|
|
87
|
+
if (!formData.parentConsent) {
|
|
88
|
+
errors.parentConsent.__errors = ['Parent consent required'];
|
|
432
89
|
}
|
|
433
90
|
}
|
|
434
91
|
}
|
|
435
|
-
];
|
|
436
|
-
|
|
437
|
-
// In your component
|
|
438
|
-
function MyForm({ schema }) {
|
|
439
|
-
const [formData, setFormData] = useState({});
|
|
440
|
-
|
|
441
|
-
// 1. Get the conditional validation configuration
|
|
442
|
-
const conditionalValidationConfig = useConditionalValidation(validationRules, {}, formData);
|
|
443
|
-
|
|
444
|
-
// 2. Create the memoized validate function
|
|
445
|
-
const customValidate = useMemo(
|
|
446
|
-
() => createCustomValidate(conditionalValidationConfig),
|
|
447
|
-
[conditionalValidationConfig]
|
|
448
|
-
);
|
|
449
|
-
|
|
450
|
-
// 3. Use with ExtendedForm
|
|
451
|
-
return (
|
|
452
|
-
<ExtendedForm
|
|
453
|
-
schema={schema}
|
|
454
|
-
formData={formData}
|
|
455
|
-
onChange={({ formData }) => setFormData(formData)}
|
|
456
|
-
customValidate={customValidate}
|
|
457
|
-
/>
|
|
458
|
-
);
|
|
459
|
-
}
|
|
92
|
+
}];
|
|
460
93
|
```
|
|
461
94
|
|
|
462
95
|
### Dynamic Arrays with O(N) Performance
|
|
463
96
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
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.
|
|
467
|
-
|
|
468
|
-
**How It Works:**
|
|
469
|
-
- **Per-Item Evaluation**: Each array item's conditionals are evaluated independently during render
|
|
470
|
-
- **No Rule Explosion**: Unlike v1's wildcard expansion, no rules are multiplied by array size
|
|
471
|
-
- **React-Optimized**: Leverages React's rendering cycle for optimal performance
|
|
97
|
+
Provides efficient handling of conditional logic within arrays, where N is the number of array items:
|
|
472
98
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
```tsx
|
|
99
|
+
```typescript
|
|
476
100
|
import { createDynamicArrayItemsUiSchema } from 'extended-dynamic-forms/conditionals/v2';
|
|
477
101
|
|
|
478
|
-
// Define per-item conditional logic - this function runs for each array item
|
|
479
|
-
const directorItemConditions = (director, index) => ({
|
|
480
|
-
shareholdingPercentage: {
|
|
481
|
-
'ui:widget': director.hasShareholding ? 'percentage' : 'hidden'
|
|
482
|
-
},
|
|
483
|
-
tfn: {
|
|
484
|
-
'ui:widget': director.isAustralianResident ? 'text' : 'hidden'
|
|
485
|
-
},
|
|
486
|
-
passport: {
|
|
487
|
-
'ui:widget': !director.isAustralianResident ? 'text' : 'hidden'
|
|
488
|
-
},
|
|
489
|
-
// Add visual indicator for the chairman
|
|
490
|
-
'ui:classNames': index === 0 ? 'chairman-director' : 'regular-director'
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
// Apply to your array field
|
|
494
102
|
const uiSchema = {
|
|
495
103
|
directors: {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
104
|
+
items: createDynamicArrayItemsUiSchema((director, index) => ({
|
|
105
|
+
shareholdingPercentage: {
|
|
106
|
+
'ui:widget': director.hasShareholding ? 'percentage' : 'hidden'
|
|
107
|
+
}
|
|
108
|
+
}))
|
|
499
109
|
}
|
|
500
110
|
};
|
|
501
111
|
```
|
|
502
112
|
|
|
503
|
-
|
|
113
|
+
## Multi-Step Wizard Forms
|
|
114
|
+
|
|
115
|
+
Create wizard forms with automatic step detection:
|
|
116
|
+
|
|
504
117
|
```typescript
|
|
505
|
-
|
|
506
|
-
// With 10 rules and 50 directors = 500 evaluations
|
|
118
|
+
import { WizardForm } from 'extended-dynamic-forms';
|
|
507
119
|
|
|
508
|
-
|
|
509
|
-
|
|
120
|
+
<WizardForm
|
|
121
|
+
schema={wizardSchema}
|
|
122
|
+
uiSchema={conditionalUiSchema}
|
|
123
|
+
formData={formData}
|
|
124
|
+
onStepChange={(fromStep, toStep) => {
|
|
125
|
+
console.log(`Step ${fromStep} → ${toStep}`);
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
510
128
|
```
|
|
511
129
|
|
|
512
|
-
**
|
|
513
|
-
```tsx
|
|
514
|
-
// Complex business logic with multiple conditions
|
|
515
|
-
const productItemConditions = (product, index) => {
|
|
516
|
-
const baseConfig = {
|
|
517
|
-
price: { 'ui:widget': 'currency' },
|
|
518
|
-
quantity: { 'ui:widget': 'number' }
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
// Category-specific fields
|
|
522
|
-
if (product.category === 'software') {
|
|
523
|
-
baseConfig.licenseType = { 'ui:widget': 'select' };
|
|
524
|
-
baseConfig.maintenancePeriod = { 'ui:widget': 'hidden' };
|
|
525
|
-
}
|
|
130
|
+
**Important:** Wizard forms flatten nested step data. Use flat paths in conditions:
|
|
526
131
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
132
|
+
```typescript
|
|
133
|
+
// Schema uses STEP_ prefixes
|
|
134
|
+
const schema = {
|
|
135
|
+
properties: {
|
|
136
|
+
STEP_PERSONAL: {
|
|
137
|
+
properties: {
|
|
138
|
+
hasDriversLicense: { type: 'boolean' }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
531
141
|
}
|
|
532
|
-
|
|
533
|
-
return baseConfig;
|
|
534
142
|
};
|
|
535
|
-
```
|
|
536
143
|
|
|
537
|
-
|
|
144
|
+
// Conditions use flat paths (no STEP_ prefix)
|
|
145
|
+
condition: { "==": [{ "var": "hasDriversLicense" }, true] }
|
|
146
|
+
```
|
|
538
147
|
|
|
539
|
-
|
|
148
|
+
## Event System & Webhooks
|
|
540
149
|
|
|
541
|
-
|
|
542
|
-
// ❌ BAD - Creates new objects every render
|
|
543
|
-
function MyForm() {
|
|
544
|
-
const conditionalUiSchema = useConditionalUi(
|
|
545
|
-
[{ condition: () => true, effect: [...] }], // New array every render!
|
|
546
|
-
{ field: { 'ui:widget': 'text' } }, // New object every render!
|
|
547
|
-
formData
|
|
548
|
-
);
|
|
549
|
-
}
|
|
150
|
+
Central event orchestration for all form interactions:
|
|
550
151
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
152
|
+
```typescript
|
|
153
|
+
<ExtendedForm
|
|
154
|
+
schema={schema}
|
|
155
|
+
uiSchema={uiSchema}
|
|
156
|
+
webhooks={[{
|
|
157
|
+
url: 'https://api.example.com/events',
|
|
158
|
+
events: ['change', 'blur'],
|
|
159
|
+
debounceMs: 500,
|
|
160
|
+
retries: 3
|
|
161
|
+
}]}
|
|
162
|
+
onFieldChange={(event) => console.log(event.fieldId, event.fieldValue)}
|
|
163
|
+
/>
|
|
558
164
|
```
|
|
559
165
|
|
|
560
|
-
|
|
166
|
+
**Event Types:**
|
|
167
|
+
- `focus` - Field focus
|
|
168
|
+
- `blur` - Field blur
|
|
169
|
+
- `change` - Value changes
|
|
170
|
+
- `submit` - Form submission
|
|
171
|
+
- `stepChange` - Wizard navigation
|
|
172
|
+
- `validationError` - Validation failures
|
|
561
173
|
|
|
562
|
-
|
|
174
|
+
## Custom Widgets
|
|
563
175
|
|
|
564
|
-
|
|
565
|
-
import { JsonLogicRule, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
176
|
+
All widgets must integrate with the event system:
|
|
566
177
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
condition: { "==": [{ "var": "fieldA" }, true] } as JsonLogicRule,
|
|
571
|
-
effect: [{ op: 'remove', path: '/fieldB/ui:widget' }]
|
|
572
|
-
};
|
|
178
|
+
```typescript
|
|
179
|
+
import { WidgetProps } from '@rjsf/utils';
|
|
180
|
+
import { useFieldEventHandlers } from 'extended-dynamic-forms/events';
|
|
573
181
|
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
}
|
|
583
|
-
effect: [{ op: 'remove', path: '/advancedFeatures/ui:widget' }]
|
|
182
|
+
export const CustomWidget: FC<WidgetProps> = ({ id, value, onChange, schema }) => {
|
|
183
|
+
const fieldEventHandlers = useFieldEventHandlers(id, schema);
|
|
184
|
+
|
|
185
|
+
const handleChange = async (newValue) => {
|
|
186
|
+
onChange(newValue); // RJSF update
|
|
187
|
+
await fieldEventHandlers.onChange(newValue); // Event system
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return <Input value={value} onChange={(e) => handleChange(e.target.value)} />;
|
|
584
191
|
};
|
|
585
192
|
```
|
|
586
193
|
|
|
587
|
-
##
|
|
194
|
+
## JsonLogic Migration Examples
|
|
588
195
|
|
|
589
|
-
|
|
196
|
+
Common patterns for migrating from function-based conditions:
|
|
590
197
|
|
|
591
|
-
|
|
198
|
+
```typescript
|
|
199
|
+
// Equality
|
|
200
|
+
// Before: (formData) => formData.field === "value"
|
|
201
|
+
// After: { "==": [{ "var": "field" }, "value"] }
|
|
592
202
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
import { useConditionalUi, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
597
|
-
import schema from './schema.json';
|
|
598
|
-
import baseUiSchema from './ui-schema.json';
|
|
599
|
-
import webhooks from './webhooks.json';
|
|
600
|
-
|
|
601
|
-
// Example: v2 Three Pillars approach with JsonLogic (required)
|
|
602
|
-
const uiRules: UiRule[] = [
|
|
603
|
-
{
|
|
604
|
-
name: 'Show tax ID in company step when listed',
|
|
605
|
-
condition: {
|
|
606
|
-
"==": [{ "var": "STEP_COMPANY.isListedCompany" }, true]
|
|
607
|
-
} as JsonLogicRule,
|
|
608
|
-
effect: [{ op: 'remove', path: '/STEP_COMPANY/taxId/ui:widget' }]
|
|
609
|
-
},
|
|
610
|
-
{
|
|
611
|
-
name: 'Show shareholding percentage for directors with shares',
|
|
612
|
-
condition: {
|
|
613
|
-
"some": [
|
|
614
|
-
{ "var": "STEP_DIRECTORS.directors" },
|
|
615
|
-
{ "var": "hasShareholding" }
|
|
616
|
-
]
|
|
617
|
-
} as JsonLogicRule,
|
|
618
|
-
effect: [{ op: 'remove', path: '/STEP_DIRECTORS/directors/items/shareholdingPercentage/ui:widget' }]
|
|
619
|
-
}
|
|
620
|
-
];
|
|
621
|
-
|
|
622
|
-
function App() {
|
|
623
|
-
const [formData, setFormData] = useState({});
|
|
624
|
-
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
625
|
-
|
|
626
|
-
return (
|
|
627
|
-
<WizardForm
|
|
628
|
-
schema={schema}
|
|
629
|
-
uiSchema={conditionalUiSchema}
|
|
630
|
-
formData={formData}
|
|
631
|
-
onChange={({ formData }) => setFormData(formData)}
|
|
632
|
-
webhooks={webhooks}
|
|
633
|
-
onSubmit={(data) => console.log('Submitted:', data.formData)}
|
|
634
|
-
/>
|
|
635
|
-
);
|
|
636
|
-
}
|
|
203
|
+
// Boolean check
|
|
204
|
+
// Before: (formData) => formData.isEnabled
|
|
205
|
+
// After: { "var": "isEnabled" }
|
|
637
206
|
|
|
638
|
-
//
|
|
639
|
-
|
|
640
|
-
{
|
|
641
|
-
conditions: { "STEP_COMPANY.isListedCompany": true },
|
|
642
|
-
event: { type: "show", params: { field: "STEP_COMPANY.taxId" } }
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
conditions: { "STEP_DIRECTORS.directors[*].hasShareholding": true },
|
|
646
|
-
event: { type: "show", params: { field: "STEP_DIRECTORS.directors[*].shareholdingPercentage" } }
|
|
647
|
-
}
|
|
648
|
-
];
|
|
207
|
+
// AND condition
|
|
208
|
+
// Before: (formData) => formData.a && formData.b
|
|
209
|
+
// After: { "and": [{ "var": "a" }, { "var": "b" }] }
|
|
649
210
|
|
|
650
|
-
//
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
uiSchema={uiSchema}
|
|
654
|
-
conditions={v1Conditions} // WizardForm handles step-aware transformation
|
|
655
|
-
onSubmit={(data) => console.log('Submitted:', data.formData)}
|
|
656
|
-
/>
|
|
211
|
+
// Array contains
|
|
212
|
+
// Before: (formData) => formData.roles?.includes("admin")
|
|
213
|
+
// After: { "in": ["admin", { "var": "roles" }] }
|
|
657
214
|
```
|
|
658
215
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
```tsx
|
|
662
|
-
import { ExtendedForm } from 'extended-dynamic-forms';
|
|
663
|
-
import { useConditionalUi, UiRule, JsonLogicRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
664
|
-
|
|
665
|
-
const schema = {
|
|
666
|
-
type: 'object',
|
|
667
|
-
properties: {
|
|
668
|
-
isStudent: { type: 'boolean', title: 'Are you a student?' },
|
|
669
|
-
studentId: { type: 'string', title: 'Student ID' },
|
|
670
|
-
school: { type: 'string', title: 'School Name' }
|
|
671
|
-
}
|
|
672
|
-
};
|
|
216
|
+
## Available Widgets
|
|
673
217
|
|
|
674
|
-
|
|
675
|
-
studentId: { 'ui:widget': 'text' },
|
|
676
|
-
school: { 'ui:widget': 'text' }
|
|
677
|
-
};
|
|
218
|
+
### Complete Widget List
|
|
678
219
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
} as JsonLogicRule,
|
|
686
|
-
effect: [
|
|
687
|
-
{ op: 'add', path: '/studentId/ui:widget', value: 'hidden' },
|
|
688
|
-
{ op: 'add', path: '/school/ui:widget', value: 'hidden' }
|
|
689
|
-
]
|
|
690
|
-
}
|
|
691
|
-
];
|
|
220
|
+
#### Choice Widget (Unified Selection)
|
|
221
|
+
- **ChoiceWidget** - Intelligent widget that automatically adapts based on schema:
|
|
222
|
+
- Renders as dropdown/select for regular choices
|
|
223
|
+
- Renders as radio group when `ui:widget` is "radio"
|
|
224
|
+
- Renders as checkbox group for multi-select arrays
|
|
225
|
+
- Supports static and dynamic data sources (API, WebSocket)
|
|
692
226
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
return (
|
|
698
|
-
<ExtendedForm
|
|
699
|
-
schema={schema}
|
|
700
|
-
uiSchema={conditionalUiSchema}
|
|
701
|
-
formData={formData}
|
|
702
|
-
onChange={({ formData }) => setFormData(formData)}
|
|
703
|
-
/>
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
```
|
|
227
|
+
#### Text Input Widgets
|
|
228
|
+
- **TextWidget** - Standard text input with Ant Design styling
|
|
229
|
+
- **TextareaWidget** - Multi-line text input
|
|
230
|
+
- **PasswordWidget** - Password input with visibility toggle
|
|
707
231
|
|
|
708
|
-
|
|
232
|
+
#### Specialized Input Widgets
|
|
233
|
+
- **ColorWidget** - Color picker using HTML5 color input
|
|
234
|
+
- **EmailWidget** - Email input with validation
|
|
235
|
+
- **URLWidget** - URL input with validation
|
|
236
|
+
- **TelephoneWidget** - Phone number input
|
|
237
|
+
- **NumberWidget** - Numeric input with increment/decrement controls
|
|
709
238
|
|
|
710
|
-
|
|
239
|
+
#### Visual Input Widgets
|
|
240
|
+
- **RangeWidget** / **SliderWidget** - Slider for numeric ranges
|
|
241
|
+
- **RatingWidget** - Star rating component
|
|
711
242
|
|
|
712
|
-
|
|
243
|
+
#### Date & Time Widgets
|
|
244
|
+
- **DateWidget** / **EnhancedDateWidget** - Date picker with Ant Design DatePicker
|
|
245
|
+
- **DateTimeWidget** - Combined date and time picker
|
|
713
246
|
|
|
714
|
-
####
|
|
247
|
+
#### File Upload Widgets
|
|
248
|
+
- **FileWidget** - Basic file input
|
|
249
|
+
- **FileUploadWidget** - Enhanced upload with progress, preview, and base64 support
|
|
715
250
|
|
|
716
|
-
|
|
717
|
-
// Color Picker
|
|
718
|
-
const uiSchema = {
|
|
719
|
-
favoriteColor: {
|
|
720
|
-
'ui:widget': 'color'
|
|
721
|
-
}
|
|
722
|
-
};
|
|
251
|
+
### Widget Configuration Examples
|
|
723
252
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
253
|
+
#### Basic Text Input
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"schema": {
|
|
257
|
+
"type": "string",
|
|
258
|
+
"title": "Full Name"
|
|
259
|
+
},
|
|
260
|
+
"uiSchema": {
|
|
261
|
+
"ui:widget": "text",
|
|
262
|
+
"ui:placeholder": "Enter your full name"
|
|
731
263
|
}
|
|
732
|
-
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
733
266
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
267
|
+
#### Number Input with Range
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"schema": {
|
|
271
|
+
"type": "number",
|
|
272
|
+
"title": "Age",
|
|
273
|
+
"minimum": 0,
|
|
274
|
+
"maximum": 120
|
|
275
|
+
},
|
|
276
|
+
"uiSchema": {
|
|
277
|
+
"ui:widget": "number"
|
|
745
278
|
}
|
|
746
|
-
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
747
281
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
282
|
+
#### Rating Widget
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"schema": {
|
|
286
|
+
"type": "number",
|
|
287
|
+
"title": "Rate your experience",
|
|
288
|
+
"minimum": 0,
|
|
289
|
+
"maximum": 5
|
|
290
|
+
},
|
|
291
|
+
"uiSchema": {
|
|
292
|
+
"ui:widget": "rating",
|
|
293
|
+
"ui:options": {
|
|
294
|
+
"count": 5,
|
|
295
|
+
"allowHalf": true
|
|
755
296
|
}
|
|
756
297
|
}
|
|
757
|
-
}
|
|
298
|
+
}
|
|
758
299
|
```
|
|
759
300
|
|
|
760
|
-
####
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
- **Visual Design**: Clean card-based layout for array management
|
|
774
|
-
- **Note**: Drag-and-drop reordering is not currently implemented
|
|
775
|
-
|
|
776
|
-
## Central Event Orchestration System
|
|
777
|
-
|
|
778
|
-
### Field-Level Event Handling
|
|
779
|
-
|
|
780
|
-
```tsx
|
|
781
|
-
import { ExtendedForm, type FormFieldEvent } from 'extended-dynamic-forms';
|
|
782
|
-
|
|
783
|
-
<ExtendedForm
|
|
784
|
-
schema={schema}
|
|
785
|
-
uiSchema={uiSchema}
|
|
786
|
-
formData={formData}
|
|
787
|
-
|
|
788
|
-
// Field-level event handlers
|
|
789
|
-
onFieldFocus={(event: FormFieldEvent) => {
|
|
790
|
-
console.log(`Field ${event.fieldId} focused:`, event.fieldValue);
|
|
791
|
-
}}
|
|
792
|
-
|
|
793
|
-
onFieldChange={(event: FormFieldEvent) => {
|
|
794
|
-
console.log(`Field ${event.fieldId} changed:`, event.fieldValue);
|
|
795
|
-
// Real-time validation or processing
|
|
796
|
-
}}
|
|
797
|
-
|
|
798
|
-
onFieldBlur={(event: FormFieldEvent) => {
|
|
799
|
-
console.log(`Field ${event.fieldId} blurred:`, event.fieldValue);
|
|
800
|
-
}}
|
|
801
|
-
/>
|
|
301
|
+
#### Choice Widget (Dropdown)
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"schema": {
|
|
305
|
+
"type": "string",
|
|
306
|
+
"title": "Country",
|
|
307
|
+
"enum": ["us", "uk", "au", "ca"],
|
|
308
|
+
"enumNames": ["United States", "United Kingdom", "Australia", "Canada"]
|
|
309
|
+
},
|
|
310
|
+
"uiSchema": {
|
|
311
|
+
"ui:widget": "choice"
|
|
312
|
+
}
|
|
313
|
+
}
|
|
802
314
|
```
|
|
803
315
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
{
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
debounceMs: 500, // Wait 500ms before sending
|
|
817
|
-
retries: 3, // Retry failed requests
|
|
818
|
-
timeout: 5000, // 5 second timeout
|
|
819
|
-
headers: {
|
|
820
|
-
'Authorization': 'Bearer your-api-token',
|
|
821
|
-
'X-Form-ID': 'contact-form'
|
|
316
|
+
#### Choice Widget (Radio Group)
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"schema": {
|
|
320
|
+
"type": "string",
|
|
321
|
+
"title": "Gender",
|
|
322
|
+
"enum": ["male", "female", "other"]
|
|
323
|
+
},
|
|
324
|
+
"uiSchema": {
|
|
325
|
+
"ui:widget": "choice",
|
|
326
|
+
"ui:options": {
|
|
327
|
+
"presenter": "radio"
|
|
822
328
|
}
|
|
823
329
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
<ExtendedForm
|
|
827
|
-
schema={schema}
|
|
828
|
-
uiSchema={uiSchema}
|
|
829
|
-
webhooks={webhookConfigs}
|
|
830
|
-
/>
|
|
330
|
+
}
|
|
831
331
|
```
|
|
832
332
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
import { ExtendedForm } from 'extended-dynamic-forms';
|
|
843
|
-
|
|
844
|
-
const conditionals = {
|
|
845
|
-
rules: [
|
|
846
|
-
{
|
|
847
|
-
conditions: { isStudent: true },
|
|
848
|
-
event: { type: "require", params: { field: ["studentId", "school"] } }
|
|
333
|
+
#### Choice Widget (Checkbox Group - Multi-select)
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"schema": {
|
|
337
|
+
"type": "array",
|
|
338
|
+
"title": "Interests",
|
|
339
|
+
"items": {
|
|
340
|
+
"type": "string",
|
|
341
|
+
"enum": ["sports", "music", "reading", "travel"]
|
|
849
342
|
},
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
343
|
+
"uniqueItems": true
|
|
344
|
+
},
|
|
345
|
+
"uiSchema": {
|
|
346
|
+
"ui:widget": "choice",
|
|
347
|
+
"ui:options": {
|
|
348
|
+
"presenter": "checkbox"
|
|
853
349
|
}
|
|
854
|
-
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
<ExtendedForm
|
|
858
|
-
schema={schema}
|
|
859
|
-
uiSchema={uiSchema}
|
|
860
|
-
conditionals={conditionals}
|
|
861
|
-
/>
|
|
862
|
-
```
|
|
863
|
-
|
|
864
|
-
### Why Migrate to v2?
|
|
865
|
-
|
|
866
|
-
- **Performance**: O(N) vs O(M×N) for arrays
|
|
867
|
-
- **Maintainability**: Clear separation of concerns
|
|
868
|
-
- **Type Safety**: Full TypeScript support
|
|
869
|
-
- **Predictability**: No complex rule interactions
|
|
870
|
-
- **Modern React**: Hooks-based architecture
|
|
871
|
-
|
|
872
|
-
## API Reference
|
|
873
|
-
|
|
874
|
-
### ExtendedFormProps
|
|
875
|
-
|
|
876
|
-
```typescript
|
|
877
|
-
interface ExtendedFormProps<T = any, S extends RJSFSchema = RJSFSchema, F = any> extends FormProps<T, S, F> {
|
|
878
|
-
// Standard RJSF props
|
|
879
|
-
schema: S;
|
|
880
|
-
uiSchema?: UiSchema<T, S, F>;
|
|
881
|
-
formData?: T;
|
|
882
|
-
onSubmit?: (data: IChangeEvent<T, S, F>, event?: React.FormEvent<HTMLFormElement>) => void;
|
|
883
|
-
onChange?: (data: IChangeEvent<T, S, F>) => void;
|
|
884
|
-
|
|
885
|
-
// Field-level event handlers
|
|
886
|
-
onFieldFocus?: (event: FormFieldEvent) => Promise<void> | void;
|
|
887
|
-
onFieldBlur?: (event: FormFieldEvent) => Promise<void> | void;
|
|
888
|
-
onFieldChange?: (event: FormFieldEvent) => Promise<void> | void;
|
|
889
|
-
|
|
890
|
-
// Webhook configuration
|
|
891
|
-
webhooks?: WebhookConfig[];
|
|
892
|
-
|
|
893
|
-
// Conditional logic (v1 - deprecated)
|
|
894
|
-
conditionals?: ConditionalConfig;
|
|
895
|
-
|
|
896
|
-
// Form lifecycle events
|
|
897
|
-
beforeSubmit?: (data: T) => Promise<T> | T;
|
|
898
|
-
afterSubmit?: (data: T) => Promise<void> | void;
|
|
899
|
-
beforeValidation?: (data: T) => Promise<T> | T;
|
|
900
|
-
afterValidation?: (data: T, errors: RJSFValidationError[]) => Promise<void> | void;
|
|
350
|
+
}
|
|
901
351
|
}
|
|
902
352
|
```
|
|
903
353
|
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
baseSchema: RJSFSchema,
|
|
918
|
-
formData: any
|
|
919
|
-
): RJSFSchema
|
|
920
|
-
|
|
921
|
-
// Validation Logic Pillar
|
|
922
|
-
function useConditionalValidation(
|
|
923
|
-
rules: ValidationRule[],
|
|
924
|
-
baseConfig: ValidationConfig,
|
|
925
|
-
formData: any
|
|
926
|
-
): ValidationConfig
|
|
927
|
-
```
|
|
928
|
-
|
|
929
|
-
## Real-World Use Cases
|
|
930
|
-
|
|
931
|
-
### 🏢 Enterprise Business Applications
|
|
932
|
-
|
|
933
|
-
**Australian Company Registration** (Featured Demo)
|
|
934
|
-
```tsx
|
|
935
|
-
// Complete 5-step wizard with dynamic conditionals
|
|
936
|
-
const companyOnboardingSchema = {
|
|
937
|
-
STEP_COMPANY: { /* Company details */ },
|
|
938
|
-
STEP_DIRECTORS: { /* Dynamic director array */ },
|
|
939
|
-
STEP_ADDRESS: { /* Address with postal logic */ },
|
|
940
|
-
STEP_FINANCIAL: { /* Banking with international support */ },
|
|
941
|
-
STEP_COMPLIANCE: { /* Regulatory requirements */ }
|
|
942
|
-
};
|
|
943
|
-
|
|
944
|
-
// Per-director conditionals with O(N) performance
|
|
945
|
-
const uiSchema = {
|
|
946
|
-
STEP_DIRECTORS: {
|
|
947
|
-
directors: {
|
|
948
|
-
items: createDynamicArrayItemsUiSchema((director) => ({
|
|
949
|
-
tfn: { 'ui:widget': director.isAustralianResident ? 'text' : 'hidden' },
|
|
950
|
-
passport: { 'ui:widget': !director.isAustralianResident ? 'text' : 'hidden' }
|
|
951
|
-
}))
|
|
354
|
+
#### Date Picker
|
|
355
|
+
```json
|
|
356
|
+
{
|
|
357
|
+
"schema": {
|
|
358
|
+
"type": "string",
|
|
359
|
+
"title": "Date of Birth",
|
|
360
|
+
"format": "date"
|
|
361
|
+
},
|
|
362
|
+
"uiSchema": {
|
|
363
|
+
"ui:widget": "date",
|
|
364
|
+
"ui:options": {
|
|
365
|
+
"format": "DD/MM/YYYY",
|
|
366
|
+
"picker": "date"
|
|
952
367
|
}
|
|
953
368
|
}
|
|
954
|
-
}
|
|
369
|
+
}
|
|
955
370
|
```
|
|
956
371
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
- Budget calculations with category-based rules
|
|
972
|
-
- Compliance checks based on funding type
|
|
973
|
-
|
|
974
|
-
### 🔄 Dynamic Array Management
|
|
975
|
-
|
|
976
|
-
**Board of Directors Management**
|
|
977
|
-
```tsx
|
|
978
|
-
// Professional-grade array handling with O(N) performance
|
|
979
|
-
import { createDynamicArrayItemsUiSchema } from 'extended-dynamic-forms';
|
|
980
|
-
|
|
981
|
-
const uiSchema = {
|
|
982
|
-
STEP_DIRECTORS: {
|
|
983
|
-
directors: {
|
|
984
|
-
items: createDynamicArrayItemsUiSchema((director, index) => ({
|
|
985
|
-
shareholdingPercentage: {
|
|
986
|
-
'ui:widget': director.hasShareholding ? 'percentage' : 'hidden'
|
|
987
|
-
},
|
|
988
|
-
tfn: {
|
|
989
|
-
'ui:widget': director.isAustralianResident ? 'text' : 'hidden'
|
|
990
|
-
},
|
|
991
|
-
passport: {
|
|
992
|
-
'ui:widget': !director.isAustralianResident ? 'text' : 'hidden'
|
|
993
|
-
}
|
|
994
|
-
}))
|
|
372
|
+
#### File Upload
|
|
373
|
+
```json
|
|
374
|
+
{
|
|
375
|
+
"schema": {
|
|
376
|
+
"type": "string",
|
|
377
|
+
"title": "Resume",
|
|
378
|
+
"format": "data-url"
|
|
379
|
+
},
|
|
380
|
+
"uiSchema": {
|
|
381
|
+
"ui:widget": "FileUploadWidget",
|
|
382
|
+
"ui:options": {
|
|
383
|
+
"accept": ".pdf,.doc,.docx",
|
|
384
|
+
"maxSize": 5242880,
|
|
385
|
+
"multiple": false
|
|
995
386
|
}
|
|
996
387
|
}
|
|
997
|
-
}
|
|
388
|
+
}
|
|
998
389
|
```
|
|
999
390
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
391
|
+
#### Color Picker
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"schema": {
|
|
395
|
+
"type": "string",
|
|
396
|
+
"title": "Favorite Color",
|
|
397
|
+
"format": "color"
|
|
398
|
+
},
|
|
399
|
+
"uiSchema": {
|
|
400
|
+
"ui:widget": "color"
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
1011
404
|
|
|
1012
|
-
|
|
1013
|
-
```
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
405
|
+
#### Range/Slider Widget
|
|
406
|
+
```json
|
|
407
|
+
{
|
|
408
|
+
"schema": {
|
|
409
|
+
"type": "number",
|
|
410
|
+
"title": "Volume",
|
|
411
|
+
"minimum": 0,
|
|
412
|
+
"maximum": 100
|
|
413
|
+
},
|
|
414
|
+
"uiSchema": {
|
|
415
|
+
"ui:widget": "range",
|
|
416
|
+
"ui:options": {
|
|
417
|
+
"marks": {
|
|
418
|
+
"0": "Mute",
|
|
419
|
+
"50": "50%",
|
|
420
|
+
"100": "Max"
|
|
421
|
+
}
|
|
1024
422
|
}
|
|
1025
|
-
}
|
|
1026
|
-
|
|
423
|
+
}
|
|
424
|
+
}
|
|
1027
425
|
```
|
|
1028
426
|
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
│ Three Pillars v2 (Conditional System) │
|
|
1041
|
-
├─────────────────────────────────────────────────┤
|
|
1042
|
-
│ Application Layer (WizardForm) │
|
|
1043
|
-
├─────────────────────────────────────────────────┤
|
|
1044
|
-
│ Event Layer (FormEventHub) │
|
|
1045
|
-
├─────────────────────────────────────────────────┤
|
|
1046
|
-
│ Component Layer (Widgets/Fields/Templates) │
|
|
1047
|
-
├─────────────────────────────────────────────────┤
|
|
1048
|
-
│ Base Layer (@rjsf/antd) │
|
|
1049
|
-
└─────────────────────────────────────────────────┘
|
|
427
|
+
#### Hidden Field
|
|
428
|
+
```json
|
|
429
|
+
{
|
|
430
|
+
"schema": {
|
|
431
|
+
"type": "string",
|
|
432
|
+
"title": "User ID"
|
|
433
|
+
},
|
|
434
|
+
"uiSchema": {
|
|
435
|
+
"ui:widget": "hidden"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
1050
438
|
```
|
|
1051
439
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
| Feature | Basic RJSF | Extended Dynamic Forms |
|
|
1055
|
-
|---------|------------|----------------------|
|
|
1056
|
-
| **Form Generation** | ✅ JSON Schema | ✅ Enhanced JSON Schema |
|
|
1057
|
-
| **UI Components** | Basic HTML | ✅ Comprehensive Ant Design Suite |
|
|
1058
|
-
| **Conditional Logic** | ❌ Manual | ✅ Three Pillars v2 (O(N)) |
|
|
1059
|
-
| **Multi-Step Forms** | ❌ Manual | ✅ Automatic Wizard Detection |
|
|
1060
|
-
| **Event System** | Basic onChange | ✅ Central Event Orchestration |
|
|
1061
|
-
| **Webhook Integration** | ❌ None | ✅ Production-Ready with Retry |
|
|
1062
|
-
| **Array Conditionals** | ❌ None | ✅ Per-Item O(N) Performance |
|
|
1063
|
-
| **TypeScript** | Partial | ✅ Comprehensive Type Definitions |
|
|
1064
|
-
| **Standalone JS** | ❌ React Only | ✅ Complete UMD Bundle |
|
|
1065
|
-
| **Testing** | Basic | ⚠️ Focused on Conditionals/Wizards |
|
|
1066
|
-
| **Performance** | Standard | ✅ Optimized with Caching |
|
|
1067
|
-
| **Validation** | Basic | ✅ Dynamic + Custom Rules |
|
|
1068
|
-
| **Developer Tools** | Limited | ✅ Interactive Playground |
|
|
440
|
+
### Widget Registration
|
|
1069
441
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
### 🚀 Quick Start
|
|
1073
|
-
|
|
1074
|
-
> **Development Setup Note**: Due to local file dependencies, you need:
|
|
1075
|
-
> 1. Clone RJSF repository in a sibling directory: `../react-jsonschema-form/`
|
|
1076
|
-
> 2. Ensure the RJSF packages are built and available
|
|
442
|
+
Register custom widgets with ExtendedForm:
|
|
1077
443
|
|
|
1078
|
-
```
|
|
1079
|
-
|
|
1080
|
-
git clone https://github.com/user/extended-dynamic-forms.git
|
|
1081
|
-
cd extended-dynamic-forms
|
|
1082
|
-
npm install # Note: Will fail without ../react-jsonschema-form/ directory
|
|
444
|
+
```typescript
|
|
445
|
+
import { ExtendedForm, customWidgets } from 'extended-dynamic-forms';
|
|
1083
446
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
447
|
+
// Use all built-in widgets
|
|
448
|
+
<ExtendedForm
|
|
449
|
+
widgets={customWidgets}
|
|
450
|
+
schema={schema}
|
|
451
|
+
uiSchema={uiSchema}
|
|
452
|
+
/>
|
|
1087
453
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
454
|
+
// Or register specific widgets
|
|
455
|
+
<ExtendedForm
|
|
456
|
+
widgets={{
|
|
457
|
+
text: customWidgets.text,
|
|
458
|
+
date: customWidgets.date,
|
|
459
|
+
choice: customWidgets.choice
|
|
460
|
+
}}
|
|
461
|
+
/>
|
|
1093
462
|
```
|
|
1094
463
|
|
|
1095
|
-
|
|
464
|
+
## Development
|
|
1096
465
|
|
|
1097
466
|
```bash
|
|
1098
|
-
#
|
|
1099
|
-
npm run
|
|
1100
|
-
npm run
|
|
1101
|
-
npm run
|
|
1102
|
-
|
|
1103
|
-
# Production validation
|
|
1104
|
-
npm run preview # Preview library build
|
|
1105
|
-
npm run serve:examples # Serve vanilla JS examples
|
|
1106
|
-
```
|
|
467
|
+
# Development
|
|
468
|
+
npm run dev # Start playground
|
|
469
|
+
npm run test # Run tests
|
|
470
|
+
npm run test:ui # Test with UI
|
|
1107
471
|
|
|
1108
|
-
|
|
472
|
+
# Building
|
|
473
|
+
npm run build # Build library
|
|
474
|
+
npm run build:vanilla # Build standalone (currently broken - JsonLogic incompatible)
|
|
475
|
+
npm run preview # Preview build
|
|
1109
476
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
- **Performance**: Array conditional optimization
|
|
1114
|
-
- **Note**: Limited coverage for UI components, event system, and utility functions
|
|
1115
|
-
|
|
1116
|
-
```bash
|
|
1117
|
-
npm run test # Run complete test suite
|
|
1118
|
-
npm run test -- --grep "conditional" # Test conditional logic
|
|
1119
|
-
npm run test -- --grep "wizard" # Test wizard forms
|
|
1120
|
-
npm run test -- --grep "array" # Test array handling
|
|
477
|
+
# Code Quality
|
|
478
|
+
npm run lint # ESLint
|
|
479
|
+
npm run format # Prettier
|
|
1121
480
|
```
|
|
1122
481
|
|
|
1123
|
-
|
|
482
|
+
## Project Structure
|
|
1124
483
|
|
|
1125
|
-
**Build Configuration**:
|
|
1126
|
-
- **Library**: ES + UMD via Vite with TypeScript declarations
|
|
1127
|
-
- **Standalone**: Complete UMD bundle with React/antd included
|
|
1128
|
-
- **External Dependencies**: React, antd marked as externals in library build
|
|
1129
|
-
- **Type Generation**: `vite-plugin-dts` for .d.ts files
|
|
1130
|
-
|
|
1131
|
-
**File Structure**:
|
|
1132
484
|
```
|
|
1133
485
|
src/
|
|
1134
|
-
├──
|
|
1135
|
-
├──
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
├── widgets/
|
|
1141
|
-
|
|
1142
|
-
└── utils/ # Utilities (Note: 76% are unused)
|
|
1143
|
-
```
|
|
1144
|
-
|
|
1145
|
-
## Examples & Demos
|
|
1146
|
-
|
|
1147
|
-
### 🎮 Interactive Playground
|
|
1148
|
-
|
|
1149
|
-
The library includes a comprehensive **interactive playground** with extensive real-world demos demonstrating all features:
|
|
1150
|
-
|
|
1151
|
-
```bash
|
|
1152
|
-
# Run the playground locally
|
|
1153
|
-
npm run dev
|
|
1154
|
-
# or
|
|
1155
|
-
npm run playground
|
|
486
|
+
├── components/ # Core form components
|
|
487
|
+
├── conditionals/ # Three Pillars system
|
|
488
|
+
│ └── v2/ # Current implementation
|
|
489
|
+
├── events/ # Event orchestration
|
|
490
|
+
├── layouts/ # Layout systems
|
|
491
|
+
│ └── wizard/ # Wizard form components
|
|
492
|
+
├── widgets/ # Input widgets
|
|
493
|
+
└── utils/ # Utilities
|
|
1156
494
|
```
|
|
1157
495
|
|
|
1158
|
-
|
|
496
|
+
## Examples
|
|
1159
497
|
|
|
1160
|
-
|
|
498
|
+
Interactive examples available at `/playground/src/demos/`:
|
|
1161
499
|
|
|
1162
|
-
|
|
1163
|
-
- **
|
|
1164
|
-
- **
|
|
1165
|
-
- **
|
|
1166
|
-
- **Migration Examples**: Automated v1-to-v2 rule conversion
|
|
500
|
+
- **Wizard Forms**: Australian Company Onboarding, Employee Onboarding
|
|
501
|
+
- **Conditionals**: Dynamic visibility, validation, array handling
|
|
502
|
+
- **Events**: Webhook integration, field tracking
|
|
503
|
+
- **Widgets**: All available components
|
|
1167
504
|
|
|
1168
|
-
|
|
1169
|
-
- **Australian Company Onboarding**: 5-step enterprise form with:
|
|
1170
|
-
- Dynamic director arrays with per-item conditionals
|
|
1171
|
-
- Real-time fee calculations based on business rules
|
|
1172
|
-
- Complex validation (ABN/ACN numbers, postcodes)
|
|
1173
|
-
- Step-aware conditional logic
|
|
1174
|
-
- Professional business workflow
|
|
505
|
+
Run `npm run dev` to explore examples.
|
|
1175
506
|
|
|
1176
|
-
|
|
1177
|
-
- **Real-time Form Analytics**: Webhook integration with retry logic
|
|
1178
|
-
- **Field-Level Event Handling**: Focus/blur/change event orchestration
|
|
1179
|
-
- **Webhook Configuration**: Multiple endpoints with custom headers
|
|
507
|
+
## Known Limitations
|
|
1180
508
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
509
|
+
1. **Dependencies**: Requires RJSF source in sibling directory
|
|
510
|
+
2. **Installation**: Cannot install from npm
|
|
511
|
+
3. **Vanilla JS**: JsonLogic not supported in standalone builds
|
|
512
|
+
4. **Arrays**: No drag-and-drop functionality
|
|
513
|
+
5. **JSON Patch**: Only supports add/remove/replace operations
|
|
514
|
+
6. **Test Coverage**: Limited for UI components and events
|
|
1185
515
|
|
|
1186
|
-
|
|
516
|
+
## TypeScript
|
|
1187
517
|
|
|
1188
|
-
|
|
518
|
+
Full TypeScript support with exported types:
|
|
1189
519
|
|
|
1190
|
-
```
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
520
|
+
```typescript
|
|
521
|
+
import {
|
|
522
|
+
ExtendedForm,
|
|
523
|
+
WizardForm,
|
|
524
|
+
UiRule,
|
|
525
|
+
SchemaRule,
|
|
526
|
+
ValidationRule,
|
|
527
|
+
JsonLogicRule,
|
|
528
|
+
FormEvent,
|
|
529
|
+
WebhookConfig
|
|
530
|
+
} from 'extended-dynamic-forms';
|
|
1195
531
|
```
|
|
1196
532
|
|
|
1197
|
-
### 🔗 Live Demo Links
|
|
1198
|
-
|
|
1199
|
-
- **[Playground](https://extended-dynamic-forms-playground.vercel.app)** - Interactive demo environment
|
|
1200
|
-
- **[Australian Company Onboarding](https://extended-dynamic-forms-playground.vercel.app/wizard/australian-company)** - Full enterprise form showcase
|
|
1201
|
-
- **[Three Pillars v2 Demo](https://extended-dynamic-forms-playground.vercel.app/conditionals/three-pillars)** - Modern conditional logic patterns
|
|
1202
|
-
|
|
1203
533
|
## Contributing
|
|
1204
534
|
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
- 🐛 [Issue Tracker](https://github.com/user/extended-dynamic-forms/issues)
|
|
1211
|
-
- 💬 [Discussions](https://github.com/user/extended-dynamic-forms/discussions)
|
|
1212
|
-
- 📧 [Email Support](mailto:support@extended-dynamic-forms.dev)
|
|
1213
|
-
|
|
1214
|
-
|
|
535
|
+
1. Fork the repository
|
|
536
|
+
2. Create your feature branch
|
|
537
|
+
3. Add tests for new functionality
|
|
538
|
+
4. Ensure all tests pass
|
|
539
|
+
5. Submit a pull request
|
|
1215
540
|
|
|
1216
|
-
|
|
541
|
+
## License
|
|
1217
542
|
|
|
1218
|
-
|
|
1219
|
-
Built with ❤️ on top of <a href="https://github.com/rjsf-team/react-jsonschema-form">React JSON Schema Form</a>
|
|
1220
|
-
</p>
|
|
543
|
+
MIT License - see LICENSE file for details.
|