extended-dynamic-forms 0.2.0 → 0.2.1
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 +427 -99
- package/dist/WizardForm.d.ts +4 -1
- package/dist/events/FormEventHub.d.ts +19 -2
- package/dist/index.es.js +6723 -6594
- package/dist/index.umd.js +22 -22
- package/dist/types/index.d.ts +6 -1
- package/dist/utils/fieldChangeDetector.d.ts +22 -0
- package/dist/vanilla-entry.d.ts +28 -0
- package/package.json +4 -4
- 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
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import Form from '@rjsf/antd';
|
|
3
|
-
import { IChangeEvent } from '@rjsf/core';
|
|
4
|
-
import { RJSFSchema, UiSchema } from '@rjsf/utils';
|
|
5
|
-
import validator from '@rjsf/validator-ajv8';
|
|
6
|
-
import {
|
|
7
|
-
Card,
|
|
8
|
-
Collapse,
|
|
9
|
-
Badge,
|
|
10
|
-
Tag,
|
|
11
|
-
Space,
|
|
12
|
-
Switch,
|
|
13
|
-
Typography,
|
|
14
|
-
Alert,
|
|
15
|
-
} from 'antd';
|
|
16
|
-
import {
|
|
17
|
-
Rule,
|
|
18
|
-
UiEffect,
|
|
19
|
-
SchemaEffect,
|
|
20
|
-
ValidationEffect,
|
|
21
|
-
JsonLogicRule,
|
|
22
|
-
} from '../src/conditionals/v2/types';
|
|
23
|
-
import {
|
|
24
|
-
useConditionalUi,
|
|
25
|
-
useConditionalSchema,
|
|
26
|
-
useConditionalValidation,
|
|
27
|
-
} from '../src/conditionals/v2';
|
|
28
|
-
import { useFormDevTools } from '../src/hooks/useFormDevTools';
|
|
29
|
-
|
|
30
|
-
const { Panel } = Collapse;
|
|
31
|
-
const { Text } = Typography;
|
|
32
|
-
|
|
33
|
-
// Base schema
|
|
34
|
-
const baseSchema: RJSFSchema = {
|
|
35
|
-
type: 'object',
|
|
36
|
-
title: 'Form with DevTools Example',
|
|
37
|
-
properties: {
|
|
38
|
-
country: {
|
|
39
|
-
type: 'string',
|
|
40
|
-
title: 'Country',
|
|
41
|
-
enum: ['USA', 'Canada', 'UK', 'Other'],
|
|
42
|
-
},
|
|
43
|
-
requiresShipping: {
|
|
44
|
-
type: 'boolean',
|
|
45
|
-
title: 'Requires Shipping?',
|
|
46
|
-
},
|
|
47
|
-
shippingAddress: {
|
|
48
|
-
type: 'object',
|
|
49
|
-
title: 'Shipping Address',
|
|
50
|
-
properties: {
|
|
51
|
-
street: { type: 'string', title: 'Street' },
|
|
52
|
-
city: { type: 'string', title: 'City' },
|
|
53
|
-
state: { type: 'string', title: 'State' },
|
|
54
|
-
zip: { type: 'string', title: 'ZIP Code' },
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
maritalStatus: {
|
|
58
|
-
type: 'string',
|
|
59
|
-
title: 'Marital Status',
|
|
60
|
-
enum: ['single', 'married', 'divorced', 'widowed'],
|
|
61
|
-
},
|
|
62
|
-
spouseName: {
|
|
63
|
-
type: 'string',
|
|
64
|
-
title: 'Spouse Name',
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// Base UI schema
|
|
70
|
-
const baseUiSchema: UiSchema = {
|
|
71
|
-
'ui:order': [
|
|
72
|
-
'country',
|
|
73
|
-
'requiresShipping',
|
|
74
|
-
'shippingAddress',
|
|
75
|
-
'maritalStatus',
|
|
76
|
-
'spouseName',
|
|
77
|
-
],
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// UI Rules
|
|
81
|
-
const uiRules: Rule<UiEffect>[] = [
|
|
82
|
-
{
|
|
83
|
-
name: 'Hide shipping address when not required',
|
|
84
|
-
condition: { "!": { "var": "requiresShipping" } } as JsonLogicRule,
|
|
85
|
-
effect: [
|
|
86
|
-
{ op: 'add', path: '/shippingAddress/ui:widget', value: 'hidden' },
|
|
87
|
-
],
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
name: 'Show state dropdown for US/Canada',
|
|
91
|
-
condition: { "or": [{ "==": [{ "var": "country" }, "USA"] }, { "==": [{ "var": "country" }, "Canada"] }] } as JsonLogicRule,
|
|
92
|
-
effect: [
|
|
93
|
-
{
|
|
94
|
-
op: 'add',
|
|
95
|
-
path: '/shippingAddress/state/ui:widget',
|
|
96
|
-
value: 'select',
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
},
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
// Schema Rules
|
|
103
|
-
const schemaRules: Rule<SchemaEffect>[] = [
|
|
104
|
-
{
|
|
105
|
-
name: 'Add spouse fields when married',
|
|
106
|
-
condition: { "===": [{ "var": "maritalStatus" }, "married"] } as JsonLogicRule,
|
|
107
|
-
effect: [{ op: 'add', path: '/required/-', value: 'spouseName' }],
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
name: 'Remove spouse field when not married',
|
|
111
|
-
condition: { "!==": [{ "var": "maritalStatus" }, "married"] } as JsonLogicRule,
|
|
112
|
-
effect: [{ op: 'remove', path: '/properties/spouseName' }],
|
|
113
|
-
},
|
|
114
|
-
];
|
|
115
|
-
|
|
116
|
-
// Validation Rules
|
|
117
|
-
const validationRules: Rule<ValidationEffect>[] = [
|
|
118
|
-
{
|
|
119
|
-
name: 'US ZIP code format validation',
|
|
120
|
-
effect: {
|
|
121
|
-
name: 'US ZIP code format',
|
|
122
|
-
isActive: { "and": [{ "==": [{ "var": "country" }, "USA"] }, { "var": "requiresShipping" }] } as JsonLogicRule,
|
|
123
|
-
validate: (errors, formData) => {
|
|
124
|
-
if (
|
|
125
|
-
formData.shippingAddress?.zip &&
|
|
126
|
-
!/^\d{5}(-\d{4})?$/.test(formData.shippingAddress.zip)
|
|
127
|
-
) {
|
|
128
|
-
if (!errors.shippingAddress) errors.shippingAddress = {};
|
|
129
|
-
if (!errors.shippingAddress.zip) errors.shippingAddress.zip = {};
|
|
130
|
-
errors.shippingAddress.zip.__errors = [
|
|
131
|
-
'ZIP code must be XXXXX or XXXXX-XXXX format',
|
|
132
|
-
];
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: 'Canadian postal code validation',
|
|
139
|
-
effect: {
|
|
140
|
-
name: 'Canadian postal code',
|
|
141
|
-
isActive: { "and": [{ "==": [{ "var": "country" }, "Canada"] }, { "var": "requiresShipping" }] } as JsonLogicRule,
|
|
142
|
-
validate: (errors, formData) => {
|
|
143
|
-
if (
|
|
144
|
-
formData.shippingAddress?.zip &&
|
|
145
|
-
!/^[A-Z]\d[A-Z] \d[A-Z]\d$/.test(formData.shippingAddress.zip)
|
|
146
|
-
) {
|
|
147
|
-
if (!errors.shippingAddress) errors.shippingAddress = {};
|
|
148
|
-
if (!errors.shippingAddress.zip) errors.shippingAddress.zip = {};
|
|
149
|
-
errors.shippingAddress.zip.__errors = [
|
|
150
|
-
'Postal code must be A1A 1A1 format',
|
|
151
|
-
];
|
|
152
|
-
}
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
// DevTools Panel Component
|
|
159
|
-
const DevToolsPanel: React.FC<{
|
|
160
|
-
devTools: ReturnType<typeof useFormDevTools>;
|
|
161
|
-
}> = ({ devTools }) => {
|
|
162
|
-
const { evaluations, activeRules, hasErrors, errors, summary } = devTools;
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<Card title='Form DevTools' style={{ marginTop: 24 }}>
|
|
166
|
-
<Space direction='vertical' style={{ width: '100%' }}>
|
|
167
|
-
{/* Summary */}
|
|
168
|
-
<Alert
|
|
169
|
-
message={
|
|
170
|
-
<Space>
|
|
171
|
-
<Text>Total Rules: {summary.totalRules}</Text>
|
|
172
|
-
<Badge
|
|
173
|
-
count={summary.activeCount}
|
|
174
|
-
style={{ backgroundColor: '#52c41a' }}
|
|
175
|
-
/>
|
|
176
|
-
<Text>Active</Text>
|
|
177
|
-
{hasErrors && (
|
|
178
|
-
<>
|
|
179
|
-
<Badge count={summary.errorCount} />
|
|
180
|
-
<Text>Errors</Text>
|
|
181
|
-
</>
|
|
182
|
-
)}
|
|
183
|
-
</Space>
|
|
184
|
-
}
|
|
185
|
-
type={hasErrors ? 'warning' : 'info'}
|
|
186
|
-
/>
|
|
187
|
-
|
|
188
|
-
{/* Errors */}
|
|
189
|
-
{hasErrors && (
|
|
190
|
-
<Alert
|
|
191
|
-
message='Rule Evaluation Errors'
|
|
192
|
-
description={
|
|
193
|
-
<ul>
|
|
194
|
-
{errors.map((error, idx) => (
|
|
195
|
-
<li key={idx}>
|
|
196
|
-
<Text type='danger'>
|
|
197
|
-
{error.pillar} Rule "{error.ruleName}": {error.error}
|
|
198
|
-
</Text>
|
|
199
|
-
</li>
|
|
200
|
-
))}
|
|
201
|
-
</ul>
|
|
202
|
-
}
|
|
203
|
-
type='error'
|
|
204
|
-
/>
|
|
205
|
-
)}
|
|
206
|
-
|
|
207
|
-
{/* Rule Details */}
|
|
208
|
-
<Collapse defaultActiveKey={['ui']}>
|
|
209
|
-
<Panel
|
|
210
|
-
header={`UI Rules (${activeRules.ui.length} active)`}
|
|
211
|
-
key='ui'
|
|
212
|
-
extra={
|
|
213
|
-
<Badge
|
|
214
|
-
count={activeRules.ui.length}
|
|
215
|
-
style={{ backgroundColor: '#52c41a' }}
|
|
216
|
-
/>
|
|
217
|
-
}
|
|
218
|
-
>
|
|
219
|
-
{evaluations
|
|
220
|
-
.filter(e => e.pillar === 'UI')
|
|
221
|
-
.map((evaluation, idx) => (
|
|
222
|
-
<div key={idx} style={{ marginBottom: 8 }}>
|
|
223
|
-
<Tag color={evaluation.active ? 'green' : 'default'}>
|
|
224
|
-
{evaluation.active ? '✓' : '○'}
|
|
225
|
-
</Tag>
|
|
226
|
-
<Text>{evaluation.ruleName}</Text>
|
|
227
|
-
{evaluation.error && (
|
|
228
|
-
<Text type='danger'> (Error: {evaluation.error})</Text>
|
|
229
|
-
)}
|
|
230
|
-
</div>
|
|
231
|
-
))}
|
|
232
|
-
</Panel>
|
|
233
|
-
|
|
234
|
-
<Panel
|
|
235
|
-
header={`Schema Rules (${activeRules.schema.length} active)`}
|
|
236
|
-
key='schema'
|
|
237
|
-
extra={
|
|
238
|
-
<Badge
|
|
239
|
-
count={activeRules.schema.length}
|
|
240
|
-
style={{ backgroundColor: '#52c41a' }}
|
|
241
|
-
/>
|
|
242
|
-
}
|
|
243
|
-
>
|
|
244
|
-
{evaluations
|
|
245
|
-
.filter(e => e.pillar === 'Schema')
|
|
246
|
-
.map((evaluation, idx) => (
|
|
247
|
-
<div key={idx} style={{ marginBottom: 8 }}>
|
|
248
|
-
<Tag color={evaluation.active ? 'green' : 'default'}>
|
|
249
|
-
{evaluation.active ? '✓' : '○'}
|
|
250
|
-
</Tag>
|
|
251
|
-
<Text>{evaluation.ruleName}</Text>
|
|
252
|
-
{evaluation.error && (
|
|
253
|
-
<Text type='danger'> (Error: {evaluation.error})</Text>
|
|
254
|
-
)}
|
|
255
|
-
</div>
|
|
256
|
-
))}
|
|
257
|
-
</Panel>
|
|
258
|
-
|
|
259
|
-
<Panel
|
|
260
|
-
header={`Validation Rules (${activeRules.validation.length} active)`}
|
|
261
|
-
key='validation'
|
|
262
|
-
extra={
|
|
263
|
-
<Badge
|
|
264
|
-
count={activeRules.validation.length}
|
|
265
|
-
style={{ backgroundColor: '#52c41a' }}
|
|
266
|
-
/>
|
|
267
|
-
}
|
|
268
|
-
>
|
|
269
|
-
{evaluations
|
|
270
|
-
.filter(e => e.pillar === 'Validation')
|
|
271
|
-
.map((evaluation, idx) => (
|
|
272
|
-
<div key={idx} style={{ marginBottom: 8 }}>
|
|
273
|
-
<Tag color={evaluation.active ? 'green' : 'default'}>
|
|
274
|
-
{evaluation.active ? '✓' : '○'}
|
|
275
|
-
</Tag>
|
|
276
|
-
<Text>{evaluation.ruleName}</Text>
|
|
277
|
-
{evaluation.error && (
|
|
278
|
-
<Text type='danger'> (Error: {evaluation.error})</Text>
|
|
279
|
-
)}
|
|
280
|
-
</div>
|
|
281
|
-
))}
|
|
282
|
-
</Panel>
|
|
283
|
-
</Collapse>
|
|
284
|
-
</Space>
|
|
285
|
-
</Card>
|
|
286
|
-
);
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
export default function FormDevToolsExample() {
|
|
290
|
-
const [formData, setFormData] = useState({});
|
|
291
|
-
const [devToolsEnabled, setDevToolsEnabled] = useState(true);
|
|
292
|
-
const [logToConsole, setLogToConsole] = useState(false);
|
|
293
|
-
const [captureHistory, setCaptureHistory] = useState(false);
|
|
294
|
-
|
|
295
|
-
// Apply conditional logic
|
|
296
|
-
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
297
|
-
const conditionalSchema = useConditionalSchema(
|
|
298
|
-
schemaRules,
|
|
299
|
-
baseSchema,
|
|
300
|
-
formData
|
|
301
|
-
);
|
|
302
|
-
const customValidate = useConditionalValidation(validationRules, formData);
|
|
303
|
-
|
|
304
|
-
// Use DevTools
|
|
305
|
-
const devTools = useFormDevTools(
|
|
306
|
-
formData,
|
|
307
|
-
uiRules,
|
|
308
|
-
schemaRules,
|
|
309
|
-
validationRules,
|
|
310
|
-
{
|
|
311
|
-
enabled: devToolsEnabled,
|
|
312
|
-
logToConsole,
|
|
313
|
-
captureHistory,
|
|
314
|
-
historySize: 10,
|
|
315
|
-
}
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
const handleChange = (e: IChangeEvent) => {
|
|
319
|
-
setFormData(e.formData);
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
return (
|
|
323
|
-
<div style={{ padding: '24px', maxWidth: '1200px', margin: '0 auto' }}>
|
|
324
|
-
<Card title='DevTools Settings' style={{ marginBottom: 24 }}>
|
|
325
|
-
<Space>
|
|
326
|
-
<label>
|
|
327
|
-
<Switch checked={devToolsEnabled} onChange={setDevToolsEnabled} />
|
|
328
|
-
<Text style={{ marginLeft: 8 }}>Enable DevTools</Text>
|
|
329
|
-
</label>
|
|
330
|
-
<label>
|
|
331
|
-
<Switch checked={logToConsole} onChange={setLogToConsole} />
|
|
332
|
-
<Text style={{ marginLeft: 8 }}>Log to Console</Text>
|
|
333
|
-
</label>
|
|
334
|
-
<label>
|
|
335
|
-
<Switch checked={captureHistory} onChange={setCaptureHistory} />
|
|
336
|
-
<Text style={{ marginLeft: 8 }}>Capture History</Text>
|
|
337
|
-
</label>
|
|
338
|
-
</Space>
|
|
339
|
-
</Card>
|
|
340
|
-
|
|
341
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24 }}>
|
|
342
|
-
<div>
|
|
343
|
-
<Form
|
|
344
|
-
schema={conditionalSchema}
|
|
345
|
-
uiSchema={conditionalUiSchema}
|
|
346
|
-
formData={formData}
|
|
347
|
-
onChange={handleChange}
|
|
348
|
-
validator={validator}
|
|
349
|
-
customValidate={customValidate}
|
|
350
|
-
/>
|
|
351
|
-
</div>
|
|
352
|
-
|
|
353
|
-
<div>
|
|
354
|
-
{devToolsEnabled && <DevToolsPanel devTools={devTools} />}
|
|
355
|
-
|
|
356
|
-
{captureHistory && devTools.history.length > 0 && (
|
|
357
|
-
<Card title='History' style={{ marginTop: 24 }}>
|
|
358
|
-
<Text>History entries: {devTools.history.length}</Text>
|
|
359
|
-
<div style={{ maxHeight: 200, overflow: 'auto', marginTop: 8 }}>
|
|
360
|
-
{devTools.history.map((entry, idx) => (
|
|
361
|
-
<div
|
|
362
|
-
key={idx}
|
|
363
|
-
style={{
|
|
364
|
-
marginBottom: 8,
|
|
365
|
-
padding: 8,
|
|
366
|
-
background: '#f0f0f0',
|
|
367
|
-
}}
|
|
368
|
-
>
|
|
369
|
-
<Text type='secondary'>
|
|
370
|
-
{new Date(entry.timestamp).toLocaleTimeString()}
|
|
371
|
-
</Text>
|
|
372
|
-
<pre style={{ fontSize: 12, margin: 0 }}>
|
|
373
|
-
{JSON.stringify(entry.formData, null, 2)}
|
|
374
|
-
</pre>
|
|
375
|
-
</div>
|
|
376
|
-
))}
|
|
377
|
-
</div>
|
|
378
|
-
</Card>
|
|
379
|
-
)}
|
|
380
|
-
</div>
|
|
381
|
-
</div>
|
|
382
|
-
</div>
|
|
383
|
-
);
|
|
384
|
-
}
|