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.
@@ -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
- }