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
package/dist/types/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FormProps } from '@rjsf/core';
|
|
2
2
|
import { RJSFSchema, UiSchema } from '@rjsf/utils';
|
|
3
3
|
|
|
4
|
+
export type WebhookEventType = 'focus' | 'blur' | 'change' | 'submit' | 'stepChange' | 'validationError';
|
|
4
5
|
export interface FormEventHandlers {
|
|
5
6
|
beforeSubmit?: (data: any) => Promise<any> | any;
|
|
6
7
|
afterSubmit?: (data: any) => Promise<void> | void;
|
|
@@ -20,7 +21,7 @@ export interface WebhookConfig {
|
|
|
20
21
|
url: string;
|
|
21
22
|
method?: 'POST' | 'PUT' | 'PATCH';
|
|
22
23
|
headers?: Record<string, string>;
|
|
23
|
-
events?:
|
|
24
|
+
events?: WebhookEventType[];
|
|
24
25
|
debounceMs?: number;
|
|
25
26
|
retries?: number;
|
|
26
27
|
timeout?: number;
|
|
@@ -34,6 +35,10 @@ export interface ExtendedFormProps extends Omit<FormProps, 'validator'>, FormEve
|
|
|
34
35
|
schema: RJSFSchema;
|
|
35
36
|
uiSchema?: UiSchema;
|
|
36
37
|
webhooks?: WebhookConfig[];
|
|
38
|
+
eventHub?: any;
|
|
39
|
+
onWebhookStart?: (webhook: WebhookConfig, event: any) => void;
|
|
40
|
+
onWebhookComplete?: (webhook: WebhookConfig, event: any, response: any) => void;
|
|
41
|
+
onWebhookError?: (webhook: WebhookConfig, event: any, error: Error) => void;
|
|
37
42
|
}
|
|
38
43
|
export interface CustomWidgetProps {
|
|
39
44
|
label?: string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for detecting field-level changes in form data
|
|
3
|
+
*/
|
|
4
|
+
export interface FieldChange {
|
|
5
|
+
fieldId: string;
|
|
6
|
+
fieldPath: string[];
|
|
7
|
+
oldValue: any;
|
|
8
|
+
newValue: any;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Deep comparison to find changed fields between two form data objects
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectFieldChanges(oldData: any, newData: any, parentPath?: string[]): FieldChange[];
|
|
14
|
+
/**
|
|
15
|
+
* Convert field path to RJSF field ID format
|
|
16
|
+
* RJSF uses a specific format like "root_fieldName_0_subField"
|
|
17
|
+
*/
|
|
18
|
+
export declare function pathToFieldId(path: string[], idPrefix?: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Extract schema for a specific field path
|
|
21
|
+
*/
|
|
22
|
+
export declare function getFieldSchema(schema: any, path: string[]): any | undefined;
|
package/dist/vanilla-entry.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { default as ReactDOM } from 'react-dom/client';
|
|
3
3
|
import { default as dayjs } from 'dayjs';
|
|
4
|
+
import { default as jsonLogic } from 'json-logic-js';
|
|
4
5
|
|
|
5
6
|
import * as antd from 'antd';
|
|
6
7
|
import * as ExtendedDynamicFormsExports from './index';
|
|
@@ -52,6 +53,32 @@ declare const ExtendedDynamicForms: {
|
|
|
52
53
|
file: string;
|
|
53
54
|
hidden: string;
|
|
54
55
|
};
|
|
56
|
+
jsonLogic: {
|
|
57
|
+
apply: typeof jsonLogic.apply;
|
|
58
|
+
add_operation: typeof jsonLogic.add_operation;
|
|
59
|
+
rm_operation: typeof jsonLogic.rm_operation;
|
|
60
|
+
equals: (field: string, value: any) => {
|
|
61
|
+
"==": any[];
|
|
62
|
+
};
|
|
63
|
+
and: (...conditions: any[]) => {
|
|
64
|
+
and: any[];
|
|
65
|
+
};
|
|
66
|
+
or: (...conditions: any[]) => {
|
|
67
|
+
or: any[];
|
|
68
|
+
};
|
|
69
|
+
not: (condition: any) => {
|
|
70
|
+
"!": any;
|
|
71
|
+
};
|
|
72
|
+
greaterThan: (field: string, value: any) => {
|
|
73
|
+
">": any[];
|
|
74
|
+
};
|
|
75
|
+
lessThan: (field: string, value: any) => {
|
|
76
|
+
"<": any[];
|
|
77
|
+
};
|
|
78
|
+
contains: (array: string, value: any) => {
|
|
79
|
+
in: any[];
|
|
80
|
+
};
|
|
81
|
+
};
|
|
55
82
|
};
|
|
56
83
|
validator: import('@rjsf/validator-ajv8/lib/validator.js').default<any, ExtendedDynamicFormsExports.RJSFSchema, any>;
|
|
57
84
|
ExtendedForm: React.ForwardRefExoticComponent<Omit<ExtendedDynamicFormsExports.ExtendedFormProps, "ref"> & React.RefAttributes<any>>;
|
|
@@ -200,5 +227,6 @@ declare const ExtendedDynamicForms: {
|
|
|
200
227
|
ReactDOM: typeof ReactDOM;
|
|
201
228
|
antd: typeof antd;
|
|
202
229
|
dayjs: typeof dayjs;
|
|
230
|
+
jsonLogic: typeof jsonLogic;
|
|
203
231
|
};
|
|
204
232
|
export default ExtendedDynamicForms;
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extended-dynamic-forms",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Extended React JSON Schema Form (RJSF) v6 with custom components, widgets, templates, layouts, and form events",
|
|
5
5
|
"main": "dist/index.umd.js",
|
|
6
6
|
"module": "dist/index.es.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
|
-
"examples",
|
|
11
10
|
"LICENSE",
|
|
12
11
|
"README.md"
|
|
13
12
|
],
|
|
@@ -41,10 +40,10 @@
|
|
|
41
40
|
"prepublishOnly": "npm run build",
|
|
42
41
|
"playground": "cd playground && npm install && npm run dev",
|
|
43
42
|
"playground:build": "cd playground && npm run build",
|
|
44
|
-
"serve:examples": "npx http-server examples/vanilla-js -p 8080 -o",
|
|
45
43
|
"setup:dev": "git clone https://github.com/rjsf-team/react-jsonschema-form ../react-jsonschema-form && cd ../react-jsonschema-form && npm install && npm run build",
|
|
46
44
|
"link:rjsf": "cd ../react-jsonschema-form/packages/core && npm link && cd ../antd && npm link && cd ../utils && npm link && cd ../validator-ajv8 && npm link",
|
|
47
|
-
"dev:install": "npm run setup:dev && npm run link:rjsf && npm install"
|
|
45
|
+
"dev:install": "npm run setup:dev && npm run link:rjsf && npm install",
|
|
46
|
+
"generate:widget": "plop --plopfile plopfile.js --dest ."
|
|
48
47
|
},
|
|
49
48
|
"keywords": [
|
|
50
49
|
"react",
|
|
@@ -93,6 +92,7 @@
|
|
|
93
92
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
94
93
|
"eslint-plugin-react-refresh": "^0.4.5",
|
|
95
94
|
"jsdom": "^26.1.0",
|
|
95
|
+
"plop": "^4.0.1",
|
|
96
96
|
"prettier": "^3.5.3",
|
|
97
97
|
"react": "^18.2.0",
|
|
98
98
|
"react-dom": "^18.2.0",
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ExtendedForm } from '../src/ExtendedForm';
|
|
3
|
-
import { RJSFSchema, UiSchema } from '@rjsf/utils';
|
|
4
|
-
import { useConditionalUi, UiRule, JsonLogicRule } from '../src/conditionals/v2';
|
|
5
|
-
|
|
6
|
-
const schema: RJSFSchema = {
|
|
7
|
-
type: 'object',
|
|
8
|
-
properties: {
|
|
9
|
-
requiresShipping: {
|
|
10
|
-
type: 'boolean',
|
|
11
|
-
title: 'Requires Shipping?'
|
|
12
|
-
},
|
|
13
|
-
shippingAddress: {
|
|
14
|
-
type: 'object',
|
|
15
|
-
title: 'Shipping Address',
|
|
16
|
-
properties: {
|
|
17
|
-
street: { type: 'string', title: 'Street' },
|
|
18
|
-
city: { type: 'string', title: 'City' },
|
|
19
|
-
zip: { type: 'string', title: 'ZIP Code' }
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const baseUiSchema: UiSchema = {
|
|
26
|
-
requiresShipping: {
|
|
27
|
-
'ui:widget': 'checkbox'
|
|
28
|
-
},
|
|
29
|
-
shippingAddress: {
|
|
30
|
-
street: { 'ui:widget': 'text' },
|
|
31
|
-
city: { 'ui:widget': 'text' },
|
|
32
|
-
zip: { 'ui:widget': 'text' }
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export function ConditionalV2Example() {
|
|
37
|
-
const [formData, setFormData] = React.useState({});
|
|
38
|
-
|
|
39
|
-
// Define UI rules
|
|
40
|
-
const uiRules: UiRule[] = [
|
|
41
|
-
{
|
|
42
|
-
name: 'Hide shipping address when not required',
|
|
43
|
-
condition: { "!": { "var": "requiresShipping" } } as JsonLogicRule,
|
|
44
|
-
effect: [
|
|
45
|
-
{ op: 'add', path: '/shippingAddress/ui:widget', value: 'hidden' }
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
// Apply conditional UI logic
|
|
51
|
-
const conditionalUiSchema = useConditionalUi(uiRules, baseUiSchema, formData);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div>
|
|
55
|
-
<h1>Conditional V2 Example - Three Pillars Architecture</h1>
|
|
56
|
-
<ExtendedForm
|
|
57
|
-
schema={schema}
|
|
58
|
-
uiSchema={conditionalUiSchema}
|
|
59
|
-
formData={formData}
|
|
60
|
-
onChange={({ formData }) => setFormData(formData)}
|
|
61
|
-
/>
|
|
62
|
-
</div>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ExtendedForm } from '../src/ExtendedForm';
|
|
3
|
-
import { RJSFSchema, UiSchema } from '@rjsf/utils';
|
|
4
|
-
import {
|
|
5
|
-
createDynamicArrayItemsUiSchema,
|
|
6
|
-
arrayItemPatterns
|
|
7
|
-
} from '../src/conditionals/v2/utils/dynamicArrayItems';
|
|
8
|
-
|
|
9
|
-
const schema: RJSFSchema = {
|
|
10
|
-
type: 'object',
|
|
11
|
-
properties: {
|
|
12
|
-
directors: {
|
|
13
|
-
type: 'array',
|
|
14
|
-
title: 'Company Directors',
|
|
15
|
-
items: {
|
|
16
|
-
type: 'object',
|
|
17
|
-
required: ['name', 'directorType'],
|
|
18
|
-
properties: {
|
|
19
|
-
name: { type: 'string', title: 'Name' },
|
|
20
|
-
directorType: {
|
|
21
|
-
type: 'string',
|
|
22
|
-
title: 'Director Type',
|
|
23
|
-
enum: ['executive', 'non-executive'],
|
|
24
|
-
enumNames: ['Executive', 'Non-Executive']
|
|
25
|
-
},
|
|
26
|
-
executiveRole: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
title: 'Executive Role',
|
|
29
|
-
enum: ['CEO', 'CFO', 'CTO', 'COO', 'Other'],
|
|
30
|
-
enumNames: ['CEO', 'CFO', 'CTO', 'COO', 'Other']
|
|
31
|
-
},
|
|
32
|
-
otherRoleDetails: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
title: 'Other Role Details'
|
|
35
|
-
},
|
|
36
|
-
hasShareholding: { type: 'boolean', title: 'Has Shareholding?' },
|
|
37
|
-
shareholdingPercentage: {
|
|
38
|
-
type: 'number',
|
|
39
|
-
title: 'Shareholding %',
|
|
40
|
-
minimum: 0,
|
|
41
|
-
maximum: 100
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Base UI schema for all array items
|
|
50
|
-
const baseItemUiSchema: UiSchema = {
|
|
51
|
-
'ui:order': ['name', 'directorType', 'executiveRole', 'otherRoleDetails', 'hasShareholding', 'shareholdingPercentage'],
|
|
52
|
-
name: {
|
|
53
|
-
'ui:widget': 'text',
|
|
54
|
-
'ui:placeholder': 'Enter director name'
|
|
55
|
-
},
|
|
56
|
-
directorType: {
|
|
57
|
-
'ui:widget': 'select'
|
|
58
|
-
},
|
|
59
|
-
executiveRole: {
|
|
60
|
-
'ui:widget': 'select'
|
|
61
|
-
},
|
|
62
|
-
otherRoleDetails: {
|
|
63
|
-
'ui:widget': 'text',
|
|
64
|
-
'ui:placeholder': 'Specify other role'
|
|
65
|
-
},
|
|
66
|
-
hasShareholding: {
|
|
67
|
-
'ui:widget': 'checkbox'
|
|
68
|
-
},
|
|
69
|
-
shareholdingPercentage: {
|
|
70
|
-
'ui:widget': 'updown'
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// Create dynamic UI schema using the utility
|
|
75
|
-
const uiSchema: UiSchema = {
|
|
76
|
-
directors: {
|
|
77
|
-
items: createDynamicArrayItemsUiSchema(
|
|
78
|
-
baseItemUiSchema,
|
|
79
|
-
(itemData, index) => {
|
|
80
|
-
const conditionalUi: Partial<UiSchema> = {};
|
|
81
|
-
|
|
82
|
-
// Three-level conditional dependency chain
|
|
83
|
-
// Level 1: Director type determines if executive role is shown
|
|
84
|
-
if (itemData?.directorType !== 'executive') {
|
|
85
|
-
conditionalUi.executiveRole = { 'ui:widget': 'hidden' };
|
|
86
|
-
// Also hide dependent field since parent is hidden
|
|
87
|
-
conditionalUi.otherRoleDetails = { 'ui:widget': 'hidden' };
|
|
88
|
-
} else {
|
|
89
|
-
// Level 2: Executive role determines if other role details is shown
|
|
90
|
-
if (itemData?.executiveRole !== 'Other') {
|
|
91
|
-
conditionalUi.otherRoleDetails = { 'ui:widget': 'hidden' };
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Hide shareholding percentage if not a shareholder
|
|
96
|
-
if (!itemData?.hasShareholding) {
|
|
97
|
-
conditionalUi.shareholdingPercentage = { 'ui:widget': 'hidden' };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Add special styling for first director
|
|
101
|
-
if (index === 0) {
|
|
102
|
-
conditionalUi['ui:classNames'] = 'primary-director';
|
|
103
|
-
conditionalUi['ui:title'] = 'Primary Director';
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return conditionalUi;
|
|
107
|
-
}
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export function DynamicArrayItemsExample() {
|
|
113
|
-
const [formData, setFormData] = React.useState({
|
|
114
|
-
directors: [
|
|
115
|
-
{
|
|
116
|
-
name: 'John Doe',
|
|
117
|
-
directorType: 'executive',
|
|
118
|
-
executiveRole: 'CEO',
|
|
119
|
-
hasShareholding: true,
|
|
120
|
-
shareholdingPercentage: 51
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: 'Jane Smith',
|
|
124
|
-
directorType: 'executive',
|
|
125
|
-
executiveRole: 'Other',
|
|
126
|
-
otherRoleDetails: 'Head of Innovation',
|
|
127
|
-
hasShareholding: true,
|
|
128
|
-
shareholdingPercentage: 30
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: 'Bob Johnson',
|
|
132
|
-
directorType: 'non-executive',
|
|
133
|
-
hasShareholding: false
|
|
134
|
-
}
|
|
135
|
-
]
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<div>
|
|
140
|
-
<h1>Dynamic Array Items Example</h1>
|
|
141
|
-
<p>
|
|
142
|
-
This example demonstrates O(n) performance for array conditionals using
|
|
143
|
-
the RJSF fork's dynamic uiSchema.items capability. It showcases:
|
|
144
|
-
</p>
|
|
145
|
-
<ul>
|
|
146
|
-
<li>Three-level conditional dependencies (Director Type → Executive Role → Other Role Details)</li>
|
|
147
|
-
<li>Enum-based conditions for more realistic form patterns</li>
|
|
148
|
-
<li>The common "Other" option pattern with additional text field</li>
|
|
149
|
-
<li>Multiple independent conditionals within each array item</li>
|
|
150
|
-
</ul>
|
|
151
|
-
|
|
152
|
-
<ExtendedForm
|
|
153
|
-
schema={schema}
|
|
154
|
-
uiSchema={uiSchema}
|
|
155
|
-
formData={formData}
|
|
156
|
-
onChange={({ formData }) => setFormData(formData)}
|
|
157
|
-
/>
|
|
158
|
-
|
|
159
|
-
<div style={{ marginTop: '20px' }}>
|
|
160
|
-
<h3>Performance Benefits:</h3>
|
|
161
|
-
<ul>
|
|
162
|
-
<li>Each array item's UI is calculated independently</li>
|
|
163
|
-
<li>No wildcard pattern expansion needed</li>
|
|
164
|
-
<li>O(n) complexity instead of O(n²)</li>
|
|
165
|
-
<li>Scales efficiently with large arrays</li>
|
|
166
|
-
</ul>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Alternative example using helper patterns
|
|
173
|
-
export function DynamicArrayPatternsExample() {
|
|
174
|
-
const uiSchemaWithPatterns: UiSchema = {
|
|
175
|
-
directors: {
|
|
176
|
-
items: createDynamicArrayItemsUiSchema(
|
|
177
|
-
baseItemUiSchema,
|
|
178
|
-
(itemData, index) => {
|
|
179
|
-
// Combine multiple pattern helpers for cleaner code
|
|
180
|
-
const hideShareholding = arrayItemPatterns.hideField(
|
|
181
|
-
'shareholdingPercentage',
|
|
182
|
-
(data) => !data?.hasShareholding
|
|
183
|
-
)(itemData, index);
|
|
184
|
-
|
|
185
|
-
const hideExecutiveRole = arrayItemPatterns.hideField(
|
|
186
|
-
'executiveRole',
|
|
187
|
-
(data) => data?.directorType !== 'executive'
|
|
188
|
-
)(itemData, index);
|
|
189
|
-
|
|
190
|
-
const hideOtherRoleDetails = arrayItemPatterns.hideField(
|
|
191
|
-
'otherRoleDetails',
|
|
192
|
-
(data) => data?.directorType !== 'executive' || data?.executiveRole !== 'Other'
|
|
193
|
-
)(itemData, index);
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
...hideShareholding,
|
|
197
|
-
...hideExecutiveRole,
|
|
198
|
-
...hideOtherRoleDetails
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
)
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
<ExtendedForm
|
|
207
|
-
schema={schema}
|
|
208
|
-
uiSchema={uiSchemaWithPatterns}
|
|
209
|
-
/>
|
|
210
|
-
);
|
|
211
|
-
}
|